]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
Split background threads into two pools.
[andy/viking.git] / src / viktrwlayer.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
a482007a
GB
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
c9570f86 8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9f30939a 9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
50a14534
EB
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
50a14534 26/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
0d2b891f 27/* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
50a14534 28
3e7553ae
GB
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
50a14534 33#include "viking.h"
7114e879 34#include "vikmapslayer.h"
e50758c7 35#include "vikgpslayer.h"
e4a11fbe 36#include "viktrwlayer_export.h"
50a14534 37#include "viktrwlayer_tpwin.h"
96c30db4 38#include "viktrwlayer_wpwin.h"
50a14534 39#include "viktrwlayer_propwin.h"
43984ba1 40#include "viktrwlayer_analysis.h"
07c9d42b 41#include "viktrwlayer_tracklist.h"
00176e85 42#include "viktrwlayer_waypointlist.h"
b3eb3b98
RN
43#ifdef VIK_CONFIG_GEOTAG
44#include "viktrwlayer_geotag.h"
45#include "geotag_exif.h"
46#endif
acaf7113 47#include "garminsymbols.h"
50a14534
EB
48#include "thumbnails.h"
49#include "background.h"
911400b5 50#include "gpx.h"
1ca082d7 51#include "geojson.h"
e0b0b9c1 52#include "babel.h"
ad0a8c2d
EB
53#include "dem.h"
54#include "dems.h"
165a4fa9 55#include "geonamessearch.h"
3e7553ae
GB
56#ifdef VIK_CONFIG_OPENSTREETMAP
57#include "osm-traces.h"
58#endif
28c82d8b 59#include "acquire.h"
16fc32f6 60#include "datasources.h"
e50758c7 61#include "datasource_gps.h"
82993cc7 62#include "vikexttool_datasources.h"
ec77010a 63#include "ui_util.h"
60626a3e 64#include "vikutils.h"
50a14534 65
9f30939a
GB
66#include "vikrouting.h"
67
bce3a7b0
EB
68#include "icons/icons.h"
69
8c00358d 70#ifdef HAVE_MATH_H
50a14534 71#include <math.h>
8c00358d
GB
72#endif
73#ifdef HAVE_STRING_H
50a14534 74#include <string.h>
8c00358d
GB
75#endif
76#ifdef HAVE_STDLIB_H
50a14534 77#include <stdlib.h>
8c00358d 78#endif
8c060406 79#include <stdio.h>
50a14534
EB
80#include <ctype.h>
81
777e2d4d 82#include <gdk/gdkkeysyms.h>
8c060406
MA
83#include <glib.h>
84#include <glib/gstdio.h>
4c77d5e0 85#include <glib/gi18n.h>
777e2d4d 86
074b1067 87#define VIK_TRW_LAYER_TRACK_GC 6
b1453c16
RN
88#define VIK_TRW_LAYER_TRACK_GCS 10
89#define VIK_TRW_LAYER_TRACK_GC_BLACK 0
90#define VIK_TRW_LAYER_TRACK_GC_SLOW 1
91#define VIK_TRW_LAYER_TRACK_GC_AVER 2
92#define VIK_TRW_LAYER_TRACK_GC_FAST 3
93#define VIK_TRW_LAYER_TRACK_GC_STOP 4
074b1067 94#define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
50a14534
EB
95
96#define DRAWMODE_BY_TRACK 0
18df6d42 97#define DRAWMODE_BY_SPEED 1
074b1067 98#define DRAWMODE_ALL_SAME_COLOR 2
18df6d42
RN
99// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
100// as we are (re)calculating the colour for every point
50a14534
EB
101
102#define POINTS 1
103#define LINES 2
104
105/* this is how it knows when you click if you are clicking close to a trackpoint. */
106#define TRACKPOINT_SIZE_APPROX 5
107#define WAYPOINT_SIZE_APPROX 5
108
b42a25ba
EB
109#define MIN_STOP_LENGTH 15
110#define MAX_STOP_LENGTH 86400
111#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
112 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
113
114enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
115
5a77ae71
RN
116// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
117typedef enum {
118 FS_XX_SMALL = 0, // 'xx-small'
119 FS_X_SMALL,
120 FS_SMALL,
121 FS_MEDIUM, // DEFAULT
122 FS_LARGE,
123 FS_X_LARGE,
124 FS_XX_LARGE,
125 FS_NUM_SIZES
126} font_size_t;
127
50a14534
EB
128struct _VikTrwLayer {
129 VikLayer vl;
130 GHashTable *tracks;
131 GHashTable *tracks_iters;
0d2b891f
RN
132 GHashTable *routes;
133 GHashTable *routes_iters;
50a14534
EB
134 GHashTable *waypoints_iters;
135 GHashTable *waypoints;
0d2b891f
RN
136 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
137 gboolean tracks_visible, routes_visible, waypoints_visible;
aa0665e5
RN
138 LatLonBBox waypoints_bbox;
139
387ff7ac 140 gboolean track_draw_labels;
50a14534
EB
141 guint8 drawmode;
142 guint8 drawpoints;
3a1c8e42 143 guint8 drawpoints_size;
b42a25ba
EB
144 guint8 drawelevation;
145 guint8 elevation_factor;
146 guint8 drawstops;
147 guint32 stop_length;
50a14534 148 guint8 drawlines;
08084371 149 guint8 drawdirections;
3a1c8e42 150 guint8 drawdirections_size;
50a14534
EB
151 guint8 line_thickness;
152 guint8 bg_line_thickness;
c9cac058 153 vik_layer_sort_order_t track_sort_order;
50a14534 154
6ba42f1e
RN
155 // Metadata
156 VikTRWMetadata *metadata;
157
387ff7ac
RN
158 PangoLayout *tracklabellayout;
159 font_size_t track_font_size;
160 gchar *track_fsize_str;
161
50a14534
EB
162 guint8 wp_symbol;
163 guint8 wp_size;
ea3933fc 164 gboolean wp_draw_symbols;
5a77ae71 165 font_size_t wp_font_size;
63ffa244 166 gchar *wp_fsize_str;
c9cac058 167 vik_layer_sort_order_t wp_sort_order;
50a14534 168
18df6d42 169 gdouble track_draw_speed_factor;
50a14534 170 GArray *track_gc;
b1453c16 171 GdkGC *track_1color_gc;
074b1067 172 GdkColor track_color;
8e9c992d 173 GdkGC *current_track_gc;
745fda83
RN
174 // Separate GC for a track's potential new point as drawn via separate method
175 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
176 GdkGC *current_track_newpoint_gc;
a7023a1b
RN
177 GdkGC *track_bg_gc; GdkColor track_bg_color;
178 GdkGC *waypoint_gc; GdkColor waypoint_color;
179 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
180 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
181 gboolean wpbgand;
0d2b891f
RN
182 GdkFont *waypoint_font;
183 VikTrack *current_track; // ATM shared between new tracks and new routes
50a14534 184 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
745fda83 185 gboolean draw_sync_done;
ef5e8132 186 gboolean draw_sync_do;
50a14534
EB
187
188 VikCoordMode coord_mode;
189
190 /* wp editing tool */
191 VikWaypoint *current_wp;
c9570f86 192 gpointer current_wp_id;
50a14534
EB
193 gboolean moving_wp;
194 gboolean waypoint_rightclick;
195
196 /* track editing tool */
197 GList *current_tpl;
ce4bd1cf
RN
198 VikTrack *current_tp_track;
199 gpointer current_tp_id;
50a14534
EB
200 VikTrwLayerTpwin *tpwin;
201
50a14534
EB
202 /* track editing tool -- more specifically, moving tps */
203 gboolean moving_tp;
204
7ff7d728
RN
205 /* route finder tool */
206 gboolean route_finder_started;
7ff7d728 207 gboolean route_finder_check_added_track;
ce4bd1cf 208 VikTrack *route_finder_added_track;
7ff7d728 209 gboolean route_finder_append;
1eef1bde 210
50a14534
EB
211 gboolean drawlabels;
212 gboolean drawimages;
213 guint8 image_alpha;
214 GQueue *image_cache;
215 guint8 image_size;
216 guint16 image_cache_size;
217
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
220
221 gboolean has_verified_thumbnails;
222
223 GtkMenu *wp_right_click_menu;
8bd81489 224 GtkMenu *track_right_click_menu;
50a14534 225
20c7a3a0
QT
226 /* menu */
227 VikStdLayerMenuItem menu_selection;
228
a8fe53f8 229 gint highest_wp_number;
43984ba1
RN
230
231 // One per layer
232 GtkWidget *tracks_analysis_dialog;
50a14534
EB
233};
234
235/* A caached waypoint image. */
236typedef struct {
237 GdkPixbuf *pixbuf;
238 gchar *image; /* filename */
239} CachedPixbuf;
240
241struct DrawingParams {
242 VikViewport *vp;
243 VikTrwLayer *vtl;
b2ddff02 244 VikWindow *vw;
50a14534
EB
245 gdouble xmpp, ympp;
246 guint16 width, height;
08084371
RN
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
50a14534 249 const VikCoord *center;
50a14534
EB
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
20981fd6 252 LatLonBBox bbox;
bc07590a 253 gboolean highlight;
50a14534
EB
254};
255
c9570f86
RN
256static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
257
1c2083ba
RN
258typedef enum {
259 MA_VTL = 0,
260 MA_VLP,
19782ffd
RN
261 MA_SUBTYPE, // OR END for Layer only
262 MA_SUBLAYER_ID,
263 MA_CONFIRM,
264 MA_VVP,
265 MA_TV_ITER,
266 MA_MISC,
267 MA_LAST,
1c2083ba
RN
268} menu_array_index;
269
19782ffd
RN
270typedef gpointer menu_array_layer[2];
271typedef gpointer menu_array_sublayer[MA_LAST];
1c2083ba 272
19782ffd
RN
273static void trw_layer_delete_item ( menu_array_sublayer values );
274static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275static void trw_layer_cut_item_cb ( menu_array_sublayer values );
33534cd8 276
9e212bfc 277static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
165a4fa9 278static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
50a14534
EB
279
280static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
282
9e212bfc
RN
283static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
50a14534 285
6bb72350 286static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
19782ffd
RN
287static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292static void trw_layer_goto_track_center ( menu_array_sublayer values );
293static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295static void trw_layer_merge_with_other ( menu_array_sublayer values );
296static void trw_layer_append_track ( menu_array_sublayer values );
297static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300static void trw_layer_split_segments ( menu_array_sublayer values );
301static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304static void trw_layer_reverse ( menu_array_sublayer values );
305static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307static void trw_layer_show_picture ( menu_array_sublayer values );
308static void trw_layer_gps_upload_any ( menu_array_sublayer values );
c95d6b00 309
1c2083ba
RN
310static void trw_layer_centerize ( menu_array_layer values );
311static void trw_layer_auto_view ( menu_array_layer values );
1c2083ba
RN
312static void trw_layer_goto_wp ( menu_array_layer values );
313static void trw_layer_new_wp ( menu_array_layer values );
314static void trw_layer_new_track ( menu_array_layer values );
315static void trw_layer_new_route ( menu_array_layer values );
316static void trw_layer_finish_track ( menu_array_layer values );
317static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318static void trw_layer_auto_tracks_view ( menu_array_layer values );
319static void trw_layer_delete_all_tracks ( menu_array_layer values );
320static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
b3eb3b98 325#ifdef VIK_CONFIG_GEOTAG
19782ffd
RN
326static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328static void trw_layer_geotagging_track ( menu_array_sublayer values );
1c2083ba 329static void trw_layer_geotagging ( menu_array_layer values );
b3eb3b98 330#endif
1c2083ba
RN
331static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333static void trw_layer_acquire_url_cb ( menu_array_layer values );
40f5740b 334#ifdef VIK_CONFIG_OPENSTREETMAP
1c2083ba
RN
335static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
40f5740b 337#endif
16fc32f6 338#ifdef VIK_CONFIG_GEOCACHES
1c2083ba 339static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
16fc32f6 340#endif
68bab1bd 341#ifdef VIK_CONFIG_GEOTAG
1c2083ba 342static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
68bab1bd 343#endif
1c2083ba
RN
344static void trw_layer_acquire_file_cb ( menu_array_layer values );
345static void trw_layer_gps_upload ( menu_array_layer values );
50a14534 346
19782ffd 347static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
1c2083ba
RN
348static void trw_layer_track_list_dialog ( menu_array_layer values );
349static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
07c9d42b 350
0d2b891f
RN
351// Specific route versions:
352// Most track handling functions can handle operating on the route list
353// However these ones are easier in separate functions
1c2083ba
RN
354static void trw_layer_auto_routes_view ( menu_array_layer values );
355static void trw_layer_delete_all_routes ( menu_array_layer values );
356static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
0d2b891f 357
50a14534 358/* pop-up items */
19782ffd
RN
359static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
50a14534 363
c9570f86 364static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
ce4bd1cf 365static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
50a14534 366
491794be 367static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
50a14534
EB
368static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 371
941aa6e9 372static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
919ed63e 373static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
33534cd8 374static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 375static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
33534cd8 376static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
377static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
379static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
919ed63e 380static void tool_edit_waypoint_destroy ( tool_ed_t *t );
7432fddf 381static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 382static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
7432fddf 383static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
e37b2a6d
RN
384static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
386static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
dc2c040e 388static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
ef5e8132 389static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
777e2d4d 390static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
941aa6e9
AF
391static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
ee43b101
MH
393static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
50a14534 396
50a14534
EB
397static void cached_pixbuf_free ( CachedPixbuf *cp );
398static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
50a14534
EB
399
400static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
402
c9570f86 403static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
9e212bfc 404static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 405
a8fe53f8
EB
406static gchar *highest_wp_number_get(VikTrwLayer *vtl);
407static void highest_wp_number_reset(VikTrwLayer *vtl);
408static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
409static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
410
79dce0cb
RN
411// Note for the following tool GtkRadioActionEntry texts:
412// the very first text value is an internal name not displayed anywhere
413// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
414// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
415// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
416// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
50a14534 417static VikToolInterface trw_layer_tools[] = {
79dce0cb
RN
418 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
419 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
ef5e8132
RN
420 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
421 FALSE,
63959706 422 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
941aa6e9 423
79dce0cb
RN
424 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
425 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
ef5e8132
RN
426 (VikToolMouseFunc) tool_new_track_click,
427 (VikToolMouseMoveFunc) tool_new_track_move,
428 (VikToolMouseFunc) tool_new_track_release,
429 (VikToolKeyFunc) tool_new_track_key_press,
430 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
63959706 431 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
50a14534 432
e37b2a6d
RN
433 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
434 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
435 (VikToolMouseFunc) tool_new_route_click,
436 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
437 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
438 (VikToolKeyFunc) tool_new_track_key_press, // -/#
439 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
63959706 440 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
98f5364d 441
ee43b101
MH
442 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
443 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
444 (VikToolMouseFunc) tool_extended_route_finder_click,
445 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
446 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
447 (VikToolKeyFunc) tool_extended_route_finder_key_press,
448 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
449 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
450
79dce0cb 451 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
919ed63e
RN
452 (VikToolConstructorFunc) tool_edit_waypoint_create,
453 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
454 NULL, NULL,
7432fddf 455 (VikToolMouseFunc) tool_edit_waypoint_click,
dc2c040e 456 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
ef5e8132
RN
457 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
458 FALSE,
63959706 459 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
941aa6e9 460
79dce0cb 461 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
919ed63e
RN
462 (VikToolConstructorFunc) tool_edit_trackpoint_create,
463 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
464 NULL, NULL,
33534cd8 465 (VikToolMouseFunc) tool_edit_trackpoint_click,
dc2c040e 466 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
ef5e8132
RN
467 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
468 FALSE,
63959706 469 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
941aa6e9 470
79dce0cb
RN
471 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
472 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
ef5e8132
RN
473 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
474 FALSE,
63959706 475 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
1eef1bde 476
941aa6e9 477};
37baade6
RN
478
479enum {
480 TOOL_CREATE_WAYPOINT=0,
481 TOOL_CREATE_TRACK,
482 TOOL_CREATE_ROUTE,
ee43b101 483 TOOL_ROUTE_FINDER,
37baade6
RN
484 TOOL_EDIT_WAYPOINT,
485 TOOL_EDIT_TRACKPOINT,
486 TOOL_SHOW_PICTURE,
37baade6
RN
487 NUM_TOOLS
488};
50a14534
EB
489
490/****** PARAMETERS ******/
491
6ba42f1e
RN
492static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
493enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
50a14534 494
074b1067 495static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
4c77d5e0 496static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
50a14534 497
3a1c8e42
RN
498#define MIN_POINT_SIZE 2
499#define MAX_POINT_SIZE 10
500
501#define MIN_ARROW_SIZE 3
502#define MAX_ARROW_SIZE 20
b42a25ba 503
50a14534
EB
504static VikLayerParamScale params_scales[] = {
505 /* min max step digits */
506 { 1, 10, 1, 0 }, /* line_thickness */
18df6d42
RN
507 { 0, 100, 1, 0 }, /* track draw speed factor */
508 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
50a14534 509 /* 5 * step == how much to turn */
3a1c8e42
RN
510 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
511 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
512 { 5, 500, 5, 0 }, // 5: image cache_size - " "
513 { 0, 8, 1, 0 }, // 6: Background line thickness
50a14534 514 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba 515 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
3a1c8e42
RN
516 { 1, 100, 1, 0 }, // 9: elevation factor
517 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
518 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
50a14534
EB
519};
520
5a77ae71
RN
521static gchar* params_font_sizes[] = {
522 N_("Extra Extra Small"),
523 N_("Extra Small"),
524 N_("Small"),
525 N_("Medium"),
526 N_("Large"),
527 N_("Extra Large"),
528 N_("Extra Extra Large"),
529 NULL };
530
c9cac058
RN
531// Needs to align with vik_layer_sort_order_t
532static gchar* params_sort_order[] = {
533 N_("None"),
534 N_("Name Ascending"),
535 N_("Name Descending"),
536 NULL
537};
538
a7023a1b
RN
539static VikLayerParamData black_color_default ( void ) {
540 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
541}
542static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
543static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
544static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
545static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
546static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
547static VikLayerParamData trackbgcolor_default ( void ) {
548 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
549}
550static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
551static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
552static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
553
387ff7ac 554static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
a7023a1b
RN
555static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
556static VikLayerParamData wptextcolor_default ( void ) {
557 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
558}
559static VikLayerParamData wpbgcolor_default ( void ) {
560 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
561}
562static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
563static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
564
565static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
566static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
567static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
568
c9cac058
RN
569static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
570
6ba42f1e
RN
571static VikLayerParamData string_default ( void )
572{
573 VikLayerParamData data;
574 data.s = "";
575 return data;
576}
577
50a14534 578VikLayerParam trw_layer_params[] = {
a87f8fa1
RN
579 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
580 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
581 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
a7023a1b 582
387ff7ac
RN
583 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
584 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
d1496bfc 585 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
a87f8fa1 586 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
a7023a1b 587 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
a87f8fa1
RN
588 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
589 { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
d1496bfc 590 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
a87f8fa1 591 { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
d1496bfc 592 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
a87f8fa1 593 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
d1496bfc 594 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
a87f8fa1 595 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
d1496bfc 596 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
a7023a1b 597 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
a87f8fa1 598 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
d1496bfc 599 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
a7023a1b 600
d1496bfc
RN
601 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
602 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
603 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
a87f8fa1 604 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
d1496bfc 605 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
a87f8fa1
RN
606
607 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
608 { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
609 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
610 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
611 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
612 { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
613 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
614 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
615 { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
616 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
617
618 { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
619 { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL, image_size_default, NULL, NULL },
620 { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
621 { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
6ba42f1e
RN
622
623 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
625 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
626 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
50a14534
EB
627};
628
3a1c8e42 629// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
08084371
RN
630enum {
631 // Sublayer visibilities
632 PARAM_TV,
633 PARAM_WV,
0d2b891f 634 PARAM_RV,
08084371 635 // Tracks
387ff7ac
RN
636 PARAM_TDL,
637 PARAM_TLFONTSIZE,
08084371 638 PARAM_DM,
074b1067 639 PARAM_TC,
08084371 640 PARAM_DL,
3a1c8e42 641 PARAM_LT,
08084371 642 PARAM_DD,
3a1c8e42 643 PARAM_DDS,
08084371 644 PARAM_DP,
3a1c8e42 645 PARAM_DPS,
08084371
RN
646 PARAM_DE,
647 PARAM_EF,
648 PARAM_DS,
649 PARAM_SL,
08084371
RN
650 PARAM_BLT,
651 PARAM_TBGC,
652 PARAM_TDSF,
c9cac058 653 PARAM_TSO,
08084371 654 // Waypoints
3a1c8e42 655 PARAM_DLA,
08084371
RN
656 PARAM_WPFONTSIZE,
657 PARAM_WPC,
658 PARAM_WPTC,
659 PARAM_WPBC,
660 PARAM_WPBA,
661 PARAM_WPSYM,
662 PARAM_WPSIZE,
663 PARAM_WPSYMS,
c9cac058 664 PARAM_WPSO,
08084371
RN
665 // WP images
666 PARAM_DI,
667 PARAM_IS,
668 PARAM_IA,
669 PARAM_ICS,
6ba42f1e
RN
670 // Metadata
671 PARAM_MDDESC,
672 PARAM_MDAUTH,
673 PARAM_MDTIME,
674 PARAM_MDKEYS,
08084371
RN
675 NUM_PARAMS
676};
ad0a8c2d
EB
677
678/*** TO ADD A PARAM:
679 *** 1) Add to trw_layer_params and enumeration
680 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
ad0a8c2d 681 ***/
50a14534
EB
682
683/****** END PARAMETERS ******/
684
a7cd93ac
RN
685/* Layer Interface function definitions */
686static VikTrwLayer* trw_layer_create ( VikViewport *vp );
687static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
20981fd6 688static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
a7cd93ac
RN
689static void trw_layer_free ( VikTrwLayer *trwlayer );
690static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
691static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
692static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
693static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
694static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
695static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
696static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
697static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
698static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
699static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
700static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
701static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
702static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
703static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
704static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
db43cfa4 705static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
a7cd93ac
RN
706static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
707static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
708static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
709static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
710static void trw_layer_free_copied_item ( gint subtype, gpointer item );
711static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
712static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
c29a3198 713static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
a7cd93ac
RN
714static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
715static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
716/* End Layer Interface function definitions */
717
50a14534 718VikLayerInterface vik_trw_layer_interface = {
db386630 719 "TrackWaypoint",
affcc0f2 720 N_("TrackWaypoint"),
75078768 721 "<control><shift>Y",
5bfafde9 722 &viktrwlayer_pixbuf,
50a14534
EB
723
724 trw_layer_tools,
725 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
726
727 trw_layer_params,
728 NUM_PARAMS,
729 params_groups, /* params_groups */
730 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
731
5a4a28bf
QT
732 VIK_MENU_ITEM_ALL,
733
a7cd93ac
RN
734 (VikLayerFuncCreate) trw_layer_create,
735 (VikLayerFuncRealize) trw_layer_realize,
b1453c16 736 (VikLayerFuncPostRead) trw_layer_post_read,
a7cd93ac 737 (VikLayerFuncFree) trw_layer_free,
50a14534
EB
738
739 (VikLayerFuncProperties) NULL,
a7cd93ac 740 (VikLayerFuncDraw) trw_layer_draw,
50a14534
EB
741 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
742
a7cd93ac
RN
743 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
744 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
20c7a3a0 745
a7cd93ac
RN
746 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
747 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
50a14534 748
a7cd93ac
RN
749 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
750 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
c7060c4e 751 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
cb89c5a5 752 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
a7cd93ac 753 (VikLayerFuncLayerSelected) trw_layer_selected,
50a14534 754
911400b5
AF
755 (VikLayerFuncMarshall) trw_layer_marshall,
756 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
757
758 (VikLayerFuncSetParam) trw_layer_set_param,
759 (VikLayerFuncGetParam) trw_layer_get_param,
db43cfa4 760 (VikLayerFuncChangeParam) trw_layer_change_param,
50a14534
EB
761
762 (VikLayerFuncReadFileData) a_gpspoint_read_file,
763 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
764
33534cd8 765 (VikLayerFuncDeleteItem) trw_layer_del_item,
d5874ef9 766 (VikLayerFuncCutItem) trw_layer_cut_item,
50a14534
EB
767 (VikLayerFuncCopyItem) trw_layer_copy_item,
768 (VikLayerFuncPasteItem) trw_layer_paste_item,
769 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
770
771 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
77ad64fa
RN
772
773 (VikLayerFuncSelectClick) trw_layer_select_click,
08f14055
RN
774 (VikLayerFuncSelectMove) trw_layer_select_move,
775 (VikLayerFuncSelectRelease) trw_layer_select_release,
e46f259a 776 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
50a14534
EB
777};
778
be3b5aa8 779static gboolean have_diary_program = FALSE;
c9e2a471
RN
780static gchar *diary_program = NULL;
781#define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
1ca082d7 782
c9e2a471 783static gboolean have_geojson_export = FALSE;
be3b5aa8 784
e6d91291
RN
785static gboolean have_astro_program = FALSE;
786static gchar *astro_program = NULL;
787#define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
788
be3b5aa8
RN
789// NB Only performed once per program run
790static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
791{
c9e2a471
RN
792 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
793#ifdef WINDOWS
794 //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
795 diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
796#else
797 diary_program = g_strdup ( "rednotebook" );
798#endif
799 }
800 else {
801 // User specified so assume it works
802 have_diary_program = TRUE;
803 }
804
805 if ( g_find_program_in_path( diary_program ) ) {
40fd4943
RN
806 gchar *mystdout = NULL;
807 gchar *mystderr = NULL;
be3b5aa8 808 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
c9e2a471
RN
809 gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version"
810 if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) {
be3b5aa8 811 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
3e9ca49d 812 if ( mystdout )
40fd4943 813 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
3e9ca49d 814 if ( mystderr )
40fd4943 815 g_warning ("Diary: stderr: %s", mystderr );
be3b5aa8
RN
816
817 gchar **tokens = NULL;
3e9ca49d 818 if ( mystdout && g_strcmp0(mystdout, "") )
40fd4943 819 tokens = g_strsplit(mystdout, " ", 0);
3e9ca49d 820 else if ( mystderr )
40fd4943 821 tokens = g_strsplit(mystderr, " ", 0);
be3b5aa8
RN
822
823 gint num = 0;
824 gchar *token = tokens[num];
825 while ( token && num < 2 ) {
826 if (num == 1) {
827 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
828 have_diary_program = TRUE;
829 }
830 num++;
831 token = tokens[num];
832 }
833 g_strfreev ( tokens );
834 }
40fd4943
RN
835 g_free ( mystdout );
836 g_free ( mystderr );
c9e2a471 837 g_free ( cmd );
be3b5aa8 838 }
1ca082d7
RN
839
840 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
841 have_geojson_export = TRUE;
842 }
e6d91291
RN
843
844 // Astronomy Domain
845 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
846#ifdef WINDOWS
847 //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
848 astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
849#else
850 astro_program = g_strdup ( "stellarium" );
851#endif
852 }
853 else {
854 // User specified so assume it works
855 have_astro_program = TRUE;
856 }
857 if ( g_find_program_in_path( astro_program ) ) {
858 have_astro_program = TRUE;
859 }
be3b5aa8
RN
860}
861
50a14534
EB
862GType vik_trw_layer_get_type ()
863{
864 static GType vtl_type = 0;
865
866 if (!vtl_type)
867 {
868 static const GTypeInfo vtl_info =
869 {
870 sizeof (VikTrwLayerClass),
871 NULL, /* base_init */
872 NULL, /* base_finalize */
be3b5aa8 873 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
50a14534
EB
874 NULL, /* class_finalize */
875 NULL, /* class_data */
876 sizeof (VikTrwLayer),
877 0,
878 NULL /* instance init */
879 };
880 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
881 }
50a14534
EB
882 return vtl_type;
883}
884
6ba42f1e
RN
885VikTRWMetadata *vik_trw_metadata_new()
886{
887 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
888}
889
890void vik_trw_metadata_free ( VikTRWMetadata *metadata)
891{
892 g_free (metadata);
893}
894
895VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
896{
897 return vtl->metadata;
898}
899
900void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
901{
902 if ( vtl->metadata )
903 vik_trw_metadata_free ( vtl->metadata );
904 vtl->metadata = metadata;
905}
906
a77c32c8
RN
907typedef struct {
908 gboolean found;
909 const gchar *date_str;
910 const VikTrack *trk;
911 const VikWaypoint *wpt;
912 gpointer *trk_id;
913 gpointer *wpt_id;
914} date_finder_type;
915
916static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
917{
918 gchar date_buf[20];
919 date_buf[0] = '\0';
920 // Might be an easier way to compare dates rather than converting the strings all the time...
921 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
922 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
923
924 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
925 df->found = TRUE;
926 df->trk = trk;
927 df->trk_id = id;
928 }
929 }
930 return df->found;
931}
932
933static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
934{
935 gchar date_buf[20];
936 date_buf[0] = '\0';
937 // Might be an easier way to compare dates rather than converting the strings all the time...
938 if ( wpt->has_timestamp ) {
939 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
940
941 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
942 df->found = TRUE;
943 df->wpt = wpt;
944 df->wpt_id = id;
945 }
946 }
947 return df->found;
948}
949
950/**
951 * Find an item by date
952 */
953gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
954{
955 date_finder_type df;
956 df.found = FALSE;
957 df.date_str = date_str;
958 df.trk = NULL;
959 df.wpt = NULL;
960 // Only tracks ATM
961 if ( do_tracks )
962 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
963 else
964 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
965
966 if ( select && df.found ) {
967 if ( do_tracks && df.trk ) {
968 struct LatLon maxmin[2] = { {0,0}, {0,0} };
969 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
970 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
971 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
972 }
973 else if ( df.wpt ) {
be5554c5 974 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
a77c32c8
RN
975 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
976 }
977 vik_layer_emit_update ( VIK_LAYER(vtl) );
978 }
979 return df.found;
980}
981
33534cd8
AF
982static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
983{
19782ffd 984 static menu_array_sublayer values;
33534cd8
AF
985 if (!sublayer) {
986 return;
987 }
33534cd8 988
19782ffd
RN
989 gint ii;
990 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
991 values[ii] = NULL;
992
993 values[MA_VTL] = vtl;
994 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
995 values[MA_SUBLAYER_ID] = sublayer;
996 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
997
998 trw_layer_delete_item ( values );
33534cd8 999}
50a14534 1000
d5874ef9
RN
1001static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
1002{
19782ffd 1003 static menu_array_sublayer values;
d5874ef9
RN
1004 if (!sublayer) {
1005 return;
1006 }
1007
19782ffd
RN
1008 gint ii;
1009 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
1010 values[ii] = NULL;
1011
1012 values[MA_VTL] = vtl;
1013 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
1014 values[MA_SUBLAYER_ID] = sublayer;
1015 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
d5874ef9 1016
19782ffd
RN
1017 trw_layer_copy_item_cb(values);
1018 trw_layer_cut_item_cb(values);
d5874ef9
RN
1019}
1020
19782ffd 1021static void trw_layer_copy_item_cb ( menu_array_sublayer values)
2cebc318 1022{
19782ffd
RN
1023 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1024 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
1025 gpointer * sublayer = values[MA_SUBLAYER_ID];
2cebc318
QT
1026 guint8 *data = NULL;
1027 guint len;
1028
1029 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
1030
1031 if (data) {
c9570f86
RN
1032 const gchar* name;
1033 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1034 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
1035 if ( wp && wp->name )
1036 name = wp->name;
1037 else
1038 name = NULL; // Broken :(
1039 }
0d2b891f 1040 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
ce4bd1cf
RN
1041 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1042 if ( trk && trk->name )
1043 name = trk->name;
1044 else
1045 name = NULL; // Broken :(
1046 }
0d2b891f
RN
1047 else {
1048 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1049 if ( trk && trk->name )
1050 name = trk->name;
1051 else
1052 name = NULL; // Broken :(
1053 }
c9570f86 1054
2cebc318 1055 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
c9570f86 1056 subtype, len, name, data);
2cebc318
QT
1057 }
1058}
1059
19782ffd 1060static void trw_layer_cut_item_cb ( menu_array_sublayer values)
2cebc318 1061{
19782ffd
RN
1062 trw_layer_copy_item_cb(values);
1063 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1064 trw_layer_delete_item(values);
2cebc318
QT
1065}
1066
19782ffd 1067static void trw_layer_paste_item_cb ( menu_array_sublayer values)
0d80642e
RN
1068{
1069 // Slightly cheating method, routing via the panels capability
19782ffd 1070 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
0d80642e
RN
1071}
1072
ddc47a46 1073static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 1074{
ddc47a46
AF
1075 guint8 *id;
1076 guint il;
1077
1078 if (!sublayer) {
1079 *item = NULL;
1080 return;
50a14534 1081 }
ddc47a46 1082
a50640dd
RN
1083 GByteArray *ba = g_byte_array_new ();
1084
1085 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
ddc47a46 1086 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
0d2b891f 1087 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
ddc47a46 1088 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
0d2b891f
RN
1089 } else {
1090 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
50a14534 1091 }
ddc47a46 1092
a50640dd
RN
1093 g_byte_array_append ( ba, id, il );
1094
ddc47a46 1095 g_free(id);
a50640dd
RN
1096
1097 *len = ba->len;
1098 *item = ba->data;
50a14534
EB
1099}
1100
ddc47a46 1101static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 1102{
a50640dd
RN
1103 if ( !item )
1104 return FALSE;
ddc47a46 1105
a50640dd
RN
1106 gchar *name;
1107
1108 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 1109 {
ddc47a46 1110 VikWaypoint *w;
ddc47a46 1111
a50640dd 1112 w = vik_waypoint_unmarshall ( item, len );
c9570f86 1113 // When copying - we'll create a new name based on the original
9748531a 1114 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
ddc47a46 1115 vik_trw_layer_add_waypoint ( vtl, name, w );
c9570f86 1116 waypoint_convert (NULL, w, &vtl->coord_mode);
1613e468 1117 g_free ( name );
c9570f86 1118
aa0665e5
RN
1119 trw_layer_calculate_bounds_waypoints ( vtl );
1120
58cd1c43
RN
1121 // Consider if redraw necessary for the new item
1122 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
da121f9b 1123 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
1124 return TRUE;
1125 }
a50640dd 1126 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 1127 {
ddc47a46 1128 VikTrack *t;
ce4bd1cf 1129
a50640dd 1130 t = vik_track_unmarshall ( item, len );
ce4bd1cf 1131 // When copying - we'll create a new name based on the original
9748531a 1132 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
ddc47a46 1133 vik_trw_layer_add_track ( vtl, name, t );
0d2b891f 1134 vik_track_convert (t, vtl->coord_mode);
1613e468 1135 g_free ( name );
ce4bd1cf 1136
58cd1c43
RN
1137 // Consider if redraw necessary for the new item
1138 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
da121f9b 1139 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
1140 return TRUE;
1141 }
a50640dd 1142 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
1143 {
1144 VikTrack *t;
0d2b891f 1145
a50640dd 1146 t = vik_track_unmarshall ( item, len );
0d2b891f
RN
1147 // When copying - we'll create a new name based on the original
1148 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1149 vik_trw_layer_add_route ( vtl, name, t );
1150 vik_track_convert (t, vtl->coord_mode);
1613e468 1151 g_free ( name );
0d2b891f
RN
1152
1153 // Consider if redraw necessary for the new item
1154 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
da121f9b 1155 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
1156 return TRUE;
1157 }
50a14534
EB
1158 return FALSE;
1159}
1160
1161static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1162{
ddc47a46
AF
1163 if (item) {
1164 g_free(item);
50a14534
EB
1165 }
1166}
1167
158b3642 1168static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
50a14534
EB
1169{
1170 switch ( id )
1171 {
1172 case PARAM_TV: vtl->tracks_visible = data.b; break;
1173 case PARAM_WV: vtl->waypoints_visible = data.b; break;
0d2b891f 1174 case PARAM_RV: vtl->routes_visible = data.b; break;
387ff7ac
RN
1175 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1176 case PARAM_TLFONTSIZE:
1177 if ( data.u < FS_NUM_SIZES ) {
1178 vtl->track_font_size = data.u;
1179 g_free ( vtl->track_fsize_str );
1180 switch ( vtl->track_font_size ) {
1181 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1182 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1183 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1184 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1185 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1186 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1187 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1188 }
1189 }
1190 break;
50a14534 1191 case PARAM_DM: vtl->drawmode = data.u; break;
074b1067
RN
1192 case PARAM_TC:
1193 vtl->track_color = data.c;
8aff54f2 1194 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
074b1067 1195 break;
50a14534 1196 case PARAM_DP: vtl->drawpoints = data.b; break;
3a1c8e42
RN
1197 case PARAM_DPS:
1198 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1199 vtl->drawpoints_size = data.u;
1200 break;
b42a25ba
EB
1201 case PARAM_DE: vtl->drawelevation = data.b; break;
1202 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 1203 case PARAM_DL: vtl->drawlines = data.b; break;
08084371 1204 case PARAM_DD: vtl->drawdirections = data.b; break;
3a1c8e42
RN
1205 case PARAM_DDS:
1206 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1207 vtl->drawdirections_size = data.u;
1208 break;
b42a25ba
EB
1209 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1210 vtl->stop_length = data.u;
1211 break;
1212 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1213 vtl->elevation_factor = data.u;
1214 break;
50a14534
EB
1215 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1216 {
1217 vtl->line_thickness = data.u;
8aff54f2 1218 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
50a14534
EB
1219 }
1220 break;
5e610fc3 1221 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
50a14534
EB
1222 {
1223 vtl->bg_line_thickness = data.u;
8aff54f2 1224 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
50a14534
EB
1225 }
1226 break;
a7023a1b
RN
1227 case PARAM_TBGC:
1228 vtl->track_bg_color = data.c;
1229 if ( vtl->track_bg_gc )
1230 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1231 break;
18df6d42 1232 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
c9cac058 1233 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
50a14534
EB
1234 case PARAM_DLA: vtl->drawlabels = data.b; break;
1235 case PARAM_DI: vtl->drawimages = data.b; break;
1236 case PARAM_IS: if ( data.u != vtl->image_size )
1237 {
1238 vtl->image_size = data.u;
1239 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1240 g_queue_free ( vtl->image_cache );
1241 vtl->image_cache = g_queue_new ();
1242 }
1243 break;
1244 case PARAM_IA: vtl->image_alpha = data.u; break;
1245 case PARAM_ICS: vtl->image_cache_size = data.u;
1246 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1247 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1248 break;
a7023a1b
RN
1249 case PARAM_WPC:
1250 vtl->waypoint_color = data.c;
1251 if ( vtl->waypoint_gc )
1252 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1253 break;
1254 case PARAM_WPTC:
1255 vtl->waypoint_text_color = data.c;
1256 if ( vtl->waypoint_text_gc )
1257 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1258 break;
1259 case PARAM_WPBC:
1260 vtl->waypoint_bg_color = data.c;
1261 if ( vtl->waypoint_bg_gc )
1262 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1263 break;
1264 case PARAM_WPBA:
1265 vtl->wpbgand = data.b;
1266 if ( vtl->waypoint_bg_gc )
1267 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1268 break;
50a14534
EB
1269 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1270 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 1271 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
63ffa244
RN
1272 case PARAM_WPFONTSIZE:
1273 if ( data.u < FS_NUM_SIZES ) {
1274 vtl->wp_font_size = data.u;
1275 g_free ( vtl->wp_fsize_str );
1276 switch ( vtl->wp_font_size ) {
1277 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1278 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1279 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1280 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1281 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1282 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1283 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1284 }
1285 }
1286 break;
c9cac058 1287 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
6ba42f1e
RN
1288 // Metadata
1289 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1290 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1291 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1292 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1293 default: break;
50a14534
EB
1294 }
1295 return TRUE;
1296}
1297
158b3642 1298static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
50a14534
EB
1299{
1300 VikLayerParamData rv;
1301 switch ( id )
1302 {
1303 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1304 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
0d2b891f 1305 case PARAM_RV: rv.b = vtl->routes_visible; break;
387ff7ac
RN
1306 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1307 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
50a14534 1308 case PARAM_DM: rv.u = vtl->drawmode; break;
074b1067 1309 case PARAM_TC: rv.c = vtl->track_color; break;
50a14534 1310 case PARAM_DP: rv.b = vtl->drawpoints; break;
3a1c8e42 1311 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
b42a25ba
EB
1312 case PARAM_DE: rv.b = vtl->drawelevation; break;
1313 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1314 case PARAM_DS: rv.b = vtl->drawstops; break;
1315 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534 1316 case PARAM_DL: rv.b = vtl->drawlines; break;
08084371 1317 case PARAM_DD: rv.b = vtl->drawdirections; break;
3a1c8e42 1318 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
50a14534
EB
1319 case PARAM_LT: rv.u = vtl->line_thickness; break;
1320 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
50a14534
EB
1321 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1322 case PARAM_DI: rv.b = vtl->drawimages; break;
a7023a1b 1323 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
18df6d42 1324 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
c9cac058 1325 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
50a14534
EB
1326 case PARAM_IS: rv.u = vtl->image_size; break;
1327 case PARAM_IA: rv.u = vtl->image_alpha; break;
1328 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
a7023a1b
RN
1329 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1330 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1331 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1332 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
50a14534
EB
1333 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1334 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 1335 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
5a77ae71 1336 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
c9cac058 1337 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
6ba42f1e
RN
1338 // Metadata
1339 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1340 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1341 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1342 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1343 default: break;
50a14534
EB
1344 }
1345 return rv;
1346}
1347
db43cfa4
RN
1348static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1349{
1350 // This '-3' is to account for the first few parameters not in the properties
1351 const gint OFFSET = -3;
1352
1353 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1354 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1355 case PARAM_DI: {
1356 // Get new value
1357 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1358 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1359 GtkWidget **ww2 = values[UI_CHG_LABELS];
1360 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1361 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1362 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1363 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1364 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1365 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1366 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1367 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1368 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1369 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1370 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1371 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1372 break;
1373 }
1374 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1375 case PARAM_DLA: {
1376 // Get new value
1377 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1378 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1379 GtkWidget **ww2 = values[UI_CHG_LABELS];
1380 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1381 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1382 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1383 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1384 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1385 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1386 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1387 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1388 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1389 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1390 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1391 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1392 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1393 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1394 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1395 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1396 break;
1397 }
1398 // Alter sensitivity of all track colours according to the draw track mode.
1399 case PARAM_DM: {
1400 // Get new value
1401 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1402 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1403 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1404 GtkWidget **ww2 = values[UI_CHG_LABELS];
1405 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1406 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1407 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1408 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1409 break;
1410 }
6ba42f1e
RN
1411 case PARAM_MDTIME: {
1412 // Force metadata->timestamp to be always read-only for now.
1413 GtkWidget **ww = values[UI_CHG_WIDGETS];
1414 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1415 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1416 }
db43cfa4
RN
1417 // NB Since other track settings have been split across tabs,
1418 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1419 default: break;
1420 }
1421}
1422
911400b5
AF
1423static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1424{
4efcd55c 1425 guint8 *pd;
4efcd55c 1426 gint pl;
911400b5
AF
1427
1428 *data = NULL;
1429
7b956cdd
RN
1430 // Use byte arrays to store sublayer data
1431 // much like done elsewhere e.g. vik_layer_marshall_params()
1432 GByteArray *ba = g_byte_array_new ( );
1433
1434 guint8 *sl_data;
1435 guint sl_len;
1436
1437 guint object_length;
1438 guint subtype;
1439 // store:
1440 // the length of the item
1441 // the sublayer type of item
1442 // the the actual item
1443#define tlm_append(object_pointer, size, type) \
1444 subtype = (type); \
1445 object_length = (size); \
1446 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1447 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1448 g_byte_array_append ( ba, (object_pointer), object_length );
1449
1450 // Layer parameters first
1451 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1452 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1453 g_byte_array_append ( ba, pd, pl );
1454 g_free ( pd );
1455
1456 // Now sublayer data
1457 GHashTableIter iter;
1458 gpointer key, value;
1459
1460 // Waypoints
1461 g_hash_table_iter_init ( &iter, vtl->waypoints );
1462 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1463 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1464 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1465 g_free ( sl_data );
1466 }
1467
1468 // Tracks
1469 g_hash_table_iter_init ( &iter, vtl->tracks );
1470 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1471 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1472 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1473 g_free ( sl_data );
911400b5 1474 }
7b956cdd
RN
1475
1476 // Routes
1477 g_hash_table_iter_init ( &iter, vtl->routes );
1478 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1479 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1480 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1481 g_free ( sl_data );
1482 }
1483
1484#undef tlm_append
1485
1486 *data = ba->data;
1487 *len = ba->len;
911400b5
AF
1488}
1489
28612684 1490static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
911400b5 1491{
0ab35525 1492 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
28612684 1493 gint pl;
7b956cdd 1494 gint consumed_length;
911400b5 1495
7b956cdd 1496 // First the overall layer parameters
911400b5
AF
1497 memcpy(&pl, data, sizeof(pl));
1498 data += sizeof(pl);
7b956cdd 1499 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
911400b5
AF
1500 data += pl;
1501
7b956cdd
RN
1502 consumed_length = pl;
1503 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1504
1505#define tlm_size (*(gint *)data)
1506 // See marshalling above for order of how this is written
1507#define tlm_next \
1508 data += sizeof_len_and_subtype + tlm_size;
1509
1510 // Now the individual sublayers:
1511
1512 while ( *data && consumed_length < len ) {
1513 // Normally four extra bytes at the end of the datastream
1514 // (since it's a GByteArray and that's where it's length is stored)
1515 // So only attempt read when there's an actual block of sublayer data
1516 if ( consumed_length + tlm_size < len ) {
1517
1518 // Reuse pl to read the subtype from the data stream
1519 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1520
1521 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1522 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1523 gchar *name = g_strdup ( trk->name );
1524 vik_trw_layer_add_track ( vtl, name, trk );
1525 g_free ( name );
1526 }
1527 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1528 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1529 gchar *name = g_strdup ( wp->name );
1530 vik_trw_layer_add_waypoint ( vtl, name, wp );
1531 g_free ( name );
1532 }
1533 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1534 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1535 gchar *name = g_strdup ( trk->name );
1536 vik_trw_layer_add_route ( vtl, name, trk );
1537 g_free ( name );
1538 }
1539 }
1540 consumed_length += tlm_size + sizeof_len_and_subtype;
1541 tlm_next;
911400b5 1542 }
7b956cdd
RN
1543 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1544
aa0665e5
RN
1545 // Not stored anywhere else so need to regenerate
1546 trw_layer_calculate_bounds_waypoints ( vtl );
1547
7b956cdd 1548 return vtl;
911400b5
AF
1549}
1550
c9570f86
RN
1551// Keep interesting hash function at least visible
1552/*
8499a412
QT
1553static guint strcase_hash(gconstpointer v)
1554{
c9570f86 1555 // 31 bit hash function
8499a412
QT
1556 int i;
1557 const gchar *t = v;
c9570f86 1558 gchar s[128]; // malloc is too slow for reading big files
8499a412
QT
1559 gchar *p = s;
1560
1561 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1562 p[i] = toupper(t[i]);
1563 p[i] = '\0';
1564
1565 p = s;
1566 guint32 h = *p;
1567 if (h) {
1568 for (p += 1; *p != '\0'; p++)
1569 h = (h << 5) - h + *p;
1570 }
1571
1572 return h;
1573}
c9570f86 1574*/
8499a412 1575
a7023a1b
RN
1576// Stick a 1 at the end of the function name to make it more unique
1577// thus more easily searchable in a simple text editor
1578static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
50a14534
EB
1579{
1580 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
a0c65899 1581 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
50a14534 1582
c9570f86
RN
1583 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1584 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1585
1586 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1587 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1588 // and with normal PC processing capabilities - it has negligibile performance impact
1589 // This also minimized the amount of rework - as the management of the hash tables already exists.
1590
1591 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1592 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1593 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1594
1595 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1596 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
ce4bd1cf
RN
1597 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1598 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
0d2b891f
RN
1599 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1600 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
50a14534 1601
a7023a1b
RN
1602 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1603
1604 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1605
1606 // Param settings that are not available via the GUI
1607 // Force to on after processing params (which defaults them to off with a zero value)
0d2b891f 1608 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1eef1bde 1609
6ba42f1e 1610 rv->metadata = vik_trw_metadata_new ();
745fda83 1611 rv->draw_sync_done = TRUE;
ef5e8132 1612 rv->draw_sync_do = TRUE;
56afe641
RN
1613 // Everything else is 0, FALSE or NULL
1614
50a14534
EB
1615 return rv;
1616}
1617
1618
a7cd93ac 1619static void trw_layer_free ( VikTrwLayer *trwlayer )
50a14534
EB
1620{
1621 g_hash_table_destroy(trwlayer->waypoints);
2b403651 1622 g_hash_table_destroy(trwlayer->waypoints_iters);
50a14534 1623 g_hash_table_destroy(trwlayer->tracks);
2b403651
RN
1624 g_hash_table_destroy(trwlayer->tracks_iters);
1625 g_hash_table_destroy(trwlayer->routes);
1626 g_hash_table_destroy(trwlayer->routes_iters);
50a14534
EB
1627
1628 /* ODC: replace with GArray */
1629 trw_layer_free_track_gcs ( trwlayer );
1630
1631 if ( trwlayer->wp_right_click_menu )
4f14a010 1632 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
50a14534 1633
8bd81489 1634 if ( trwlayer->track_right_click_menu )
1a7a0378 1635 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
8bd81489 1636
387ff7ac
RN
1637 if ( trwlayer->tracklabellayout != NULL)
1638 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1639
50a14534
EB
1640 if ( trwlayer->wplabellayout != NULL)
1641 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1642
1643 if ( trwlayer->waypoint_gc != NULL )
1644 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1645
1646 if ( trwlayer->waypoint_text_gc != NULL )
1647 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1648
1649 if ( trwlayer->waypoint_bg_gc != NULL )
1650 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1651
63ffa244 1652 g_free ( trwlayer->wp_fsize_str );
387ff7ac 1653 g_free ( trwlayer->track_fsize_str );
63ffa244 1654
50a14534
EB
1655 if ( trwlayer->tpwin != NULL )
1656 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1657
43984ba1
RN
1658 if ( trwlayer->tracks_analysis_dialog != NULL )
1659 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1660
50a14534
EB
1661 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1662 g_queue_free ( trwlayer->image_cache );
1663}
1664
bc07590a 1665static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
50a14534 1666{
08084371 1667 dp->vtl = vtl;
50a14534 1668 dp->vp = vp;
bc07590a 1669 dp->highlight = highlight;
b2ddff02 1670 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
50a14534
EB
1671 dp->xmpp = vik_viewport_get_xmpp ( vp );
1672 dp->ympp = vik_viewport_get_ympp ( vp );
1673 dp->width = vik_viewport_get_width ( vp );
1674 dp->height = vik_viewport_get_height ( vp );
81765f5e
GB
1675 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1676 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
08084371 1677
50a14534
EB
1678 dp->center = vik_viewport_get_center ( vp );
1679 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1680 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1681
1682 if ( dp->one_zone )
1683 {
1684 gint w2, h2;
1685 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1686 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1687 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1688
1689 dp->ce1 = dp->center->east_west-w2;
1690 dp->ce2 = dp->center->east_west+w2;
1691 dp->cn1 = dp->center->north_south-h2;
1692 dp->cn2 = dp->center->north_south+h2;
1693 } else if ( dp->lat_lon ) {
1694 VikCoord upperleft, bottomright;
1695 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1696 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1697 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1698 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1699 dp->ce1 = upperleft.east_west;
1700 dp->ce2 = bottomright.east_west;
1701 dp->cn1 = bottomright.north_south;
1702 dp->cn2 = upperleft.north_south;
1703 }
20981fd6
RN
1704
1705 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
50a14534
EB
1706}
1707
18df6d42
RN
1708/*
1709 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1710 * Here a simple traffic like light colour system is used:
1711 * . slow points are red
1712 * . average is yellow
1713 * . fast points are green
1714 */
1715static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
50a14534 1716{
18df6d42
RN
1717 gdouble rv = 0;
1718 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1719 if ( average_speed > 0 ) {
1720 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1721 if ( rv < low_speed )
1722 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1723 else if ( rv > high_speed )
1724 return VIK_TRW_LAYER_TRACK_GC_FAST;
1725 else
1726 return VIK_TRW_LAYER_TRACK_GC_AVER;
1727 }
1728 }
1729 return VIK_TRW_LAYER_TRACK_GC_BLACK;
50a14534
EB
1730}
1731
5b309036 1732static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
50a14534
EB
1733{
1734 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1735 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1736 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1737 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1738}
1739
387ff7ac
RN
1740
1741static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1742{
1743 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1744
1745 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1746 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1747 else
1748 // Fallback if parse failure
1749 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1750
1751 g_free ( label_markup );
1752
1753 gint label_x, label_y;
1754 gint width, height;
1755 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1756
1757 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1758 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1759}
1760
1761/**
1762 * distance_in_preferred_units:
1763 * @dist: The source distance in standard SI Units (i.e. metres)
1764 *
1765 * TODO: This is a generic function that could be moved into globals.c or utils.c
1766 *
1767 * Probably best used if you have a only few conversions to perform.
1768 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1769 * since it will be doing the preference check on each call
1770 *
1771 * Returns: The distance in the units as specified by the preferences
1772 */
1773static gdouble distance_in_preferred_units ( gdouble dist )
1774{
1775 gdouble mydist;
1776 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1777 switch (dist_units) {
1778 case VIK_UNITS_DISTANCE_MILES:
1779 mydist = VIK_METERS_TO_MILES(dist);
1780 break;
b22233bd
RN
1781 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1782 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1783 break;
387ff7ac
RN
1784 // VIK_UNITS_DISTANCE_KILOMETRES:
1785 default:
1786 mydist = dist/1000.0;
1787 break;
1788 }
1789 return mydist;
1790}
1791
1792/**
1793 * trw_layer_draw_dist_labels:
1794 *
1795 * Draw a few labels along a track at nicely seperated distances
1796 * This might slow things down if there's many tracks being displayed with this on.
1797 */
1798static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1799{
1800 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1801 25.0, 40.0, 50.0, 75.0, 100.0,
1802 150.0, 200.0, 250.0, 500.0, 1000.0};
1803
1804 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1805
1806 // Convert to specified unit to find the friendly breakdown value
1807 dist = distance_in_preferred_units ( dist );
1808
1809 gint index = 0;
1810 gint i=0;
1811 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1812 if ( chunksd[i] > dist ) {
1813 index = i;
1814 dist = chunksd[index];
1815 break;
1816 }
1817 }
1818
1819 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1820
1821 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1822 gdouble dist_i = dist * i;
1823
1824 // Convert distance back into metres for use in finding a trackpoint
1825 switch (dist_units) {
1826 case VIK_UNITS_DISTANCE_MILES:
1827 dist_i = VIK_MILES_TO_METERS(dist_i);
1828 break;
b22233bd
RN
1829 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1830 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1831 break;
387ff7ac
RN
1832 // VIK_UNITS_DISTANCE_KILOMETRES:
1833 default:
1834 dist_i = dist_i*1000.0;
1835 break;
1836 }
1837
1838 gdouble dist_current = 0.0;
1839 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1840 gdouble dist_next = 0.0;
1841 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1842
1843 gdouble dist_between_tps = fabs (dist_next - dist_current);
1844 gdouble ratio = 0.0;
1845 // Prevent division by 0 errors
1846 if ( dist_between_tps > 0.0 )
1847 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1848
1849 if ( tp_current && tp_next ) {
1850 // Construct the name based on the distance value
1851 gchar *name;
1852 gchar *units;
1853 switch (dist_units) {
1854 case VIK_UNITS_DISTANCE_MILES:
1855 units = g_strdup ( _("miles") );
1856 break;
1857 // VIK_UNITS_DISTANCE_KILOMETRES:
1858 default:
1859 units = g_strdup ( _("km") );
1860 break;
1861 }
1862
1863 // Convert for display
1864 dist_i = distance_in_preferred_units ( dist_i );
1865
1866 // Make the precision of the output related to the unit size.
1867 if ( index == 0 )
1868 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1869 else if ( index == 1 )
1870 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1871 else
1872 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1873 g_free ( units );
1874
1875 struct LatLon ll_current, ll_next;
1876 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1877 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1878
1879 // positional interpolation
1880 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1881 // but should be good enough over the small scale that I anticipate usage on
1882 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1883 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1884 VikCoord coord;
1885 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1886
1887 gchar *fgcolour;
1888 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1889 fgcolour = gdk_color_to_string ( &(trk->color) );
1890 else
1891 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1892
1893 // if highlight mode on, then colour the background in the highlight colour
1894 gchar *bgcolour;
1895 if ( drawing_highlight )
1896 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1897 else
1898 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1899
1900 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1901
1902 g_free ( fgcolour );
1903 g_free ( bgcolour );
1904 g_free ( name );
1905 }
1906 }
1907}
1908
1909/**
1910 * trw_layer_draw_track_name_labels:
1911 *
1912 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1913 */
1914static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1915{
1916 gchar *fgcolour;
1917 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1918 fgcolour = gdk_color_to_string ( &(trk->color) );
1919 else
1920 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1921
1922 // if highlight mode on, then colour the background in the highlight colour
1923 gchar *bgcolour;
1924 if ( drawing_highlight )
1925 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1926 else
1927 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1928
1929 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1930
1931 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1932 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1933 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1934 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1935 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1936 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1937 VikCoord coord;
1938 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1939
1940 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1941 }
1942
1943 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1944 // No other labels to draw
1945 return;
1946
215ebe59
RN
1947 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1948 if ( !tp_end )
1949 return;
1950 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1951 if ( !tp_begin )
1952 return;
1953 VikCoord begin_coord = tp_begin->coord;
1954 VikCoord end_coord = tp_end->coord;
387ff7ac
RN
1955
1956 gboolean done_start_end = FALSE;
1957
1958 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1959 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1960
1961 // This number can be configured via the settings if you really want to change it
1962 gdouble distance_diff;
1963 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1964 distance_diff = 100.0; // Metres
1965
1966 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1967 // Start and end 'close' together so only draw one label at an average location
1968 gint x1, x2, y1, y2;
1969 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1970 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1971 VikCoord av_coord;
1972 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1973
1974 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1975 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1976 g_free ( name );
1977
1978 done_start_end = TRUE;
1979 }
1980 }
1981
1982 if ( ! done_start_end ) {
1983 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1984 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1985 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1986 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1987 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1988 g_free ( name_start );
1989 }
1990 // Don't draw end label if this is the one being created
1991 if ( trk != dp->vtl->current_track ) {
1992 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1993 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1994 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1995 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1996 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1997 g_free ( name_end );
1998 }
1999 }
2000 }
2001
2002 g_free ( fgcolour );
2003 g_free ( bgcolour );
2004 g_free ( ename );
2005}
2006
9e212bfc 2007static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
50a14534 2008{
20981fd6
RN
2009 if ( ! track->visible )
2010 return;
2011
50a14534
EB
2012 /* TODO: this function is a mess, get rid of any redundancy */
2013 GList *list = track->trackpoints;
8e9c992d 2014 GdkGC *main_gc;
50a14534
EB
2015 gboolean useoldvals = TRUE;
2016
2017 gboolean drawpoints;
b42a25ba
EB
2018 gboolean drawstops;
2019 gboolean drawelevation;
941aa6e9 2020 gdouble min_alt, max_alt, alt_diff = 0;
50a14534 2021
3a1c8e42
RN
2022 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
2023 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
50a14534
EB
2024 guint8 tp_size;
2025
b42a25ba
EB
2026 if ( dp->vtl->drawelevation )
2027 {
2028 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
2029 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
2030 alt_diff = max_alt - min_alt;
2031 }
2032
50a14534 2033 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
0c9b1cf7 2034 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
9e212bfc 2035 trw_layer_draw_track ( id, track, dp, TRUE );
50a14534 2036
0c9b1cf7 2037 if ( draw_track_outline )
b42a25ba
EB
2038 drawpoints = drawstops = FALSE;
2039 else {
50a14534 2040 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
2041 drawstops = dp->vtl->drawstops;
2042 }
50a14534 2043
18df6d42 2044 gboolean drawing_highlight = FALSE;
a5dcfdb7 2045 /* Current track - used for creation */
8e9c992d
EB
2046 if ( track == dp->vtl->current_track )
2047 main_gc = dp->vtl->current_track_gc;
a5dcfdb7 2048 else {
bc07590a
RN
2049 if ( dp->highlight ) {
2050 /* Draw all tracks of the layer in special colour
2051 NB this supercedes the drawmode */
2052 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2053 drawing_highlight = TRUE;
a5dcfdb7 2054 }
b1453c16
RN
2055 if ( !drawing_highlight ) {
2056 // Still need to figure out the gc according to the drawing mode:
2057 switch ( dp->vtl->drawmode ) {
2058 case DRAWMODE_BY_TRACK:
2059 if ( dp->vtl->track_1color_gc )
2060 g_object_unref ( dp->vtl->track_1color_gc );
2061 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2062 main_gc = dp->vtl->track_1color_gc;
2063 break;
2064 default:
074b1067 2065 // Mostly for DRAWMODE_ALL_SAME_COLOR
b1453c16 2066 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
074b1067 2067 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
b1453c16
RN
2068 break;
2069 }
a5dcfdb7
RN
2070 }
2071 }
8e9c992d 2072
50a14534
EB
2073 if (list) {
2074 int x, y, oldx, oldy;
2075 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2076
2077 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2078
2079 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2080
b1453c16
RN
2081 // Draw the first point as something a bit different from the normal points
2082 // ATM it's slightly bigger and a triangle
2083 if ( drawpoints ) {
50a14534 2084 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 2085 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
50a14534
EB
2086 }
2087
2088 oldx = x;
2089 oldy = y;
2090
18df6d42
RN
2091 gdouble average_speed = 0.0;
2092 gdouble low_speed = 0.0;
2093 gdouble high_speed = 0.0;
2094 // If necessary calculate these values - which is done only once per track redraw
2095 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2096 // the percentage factor away from the average speed determines transistions between the levels
2097 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2098 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2099 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2100 }
2101
50a14534
EB
2102 while ((list = g_list_next(list)))
2103 {
2104 tp = VIK_TRACKPOINT(list->data);
2105 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2106
2107 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2108 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2109 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2110 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2111 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2112 {
2113 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2114
adc271a8
GT
2115 /*
2116 * If points are the same in display coordinates, don't draw.
2117 */
2118 if ( useoldvals && x == oldx && y == oldy )
b827c5e0
RN
2119 {
2120 // Still need to process points to ensure 'stops' are drawn if required
0c9b1cf7 2121 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
b827c5e0
RN
2122 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2123 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 );
2124
adc271a8 2125 goto skip;
b827c5e0 2126 }
adc271a8 2127
18df6d42
RN
2128 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2129 if ( drawpoints || dp->vtl->drawlines ) {
2130 // setup main_gc for both point and line drawing
2131 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
b1453c16 2132 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
2133 }
2134 }
2135
0c9b1cf7 2136 if ( drawpoints && ! draw_track_outline )
50a14534 2137 {
18df6d42 2138
50a14534 2139 if ( list->next ) {
4511d71f 2140 /*
f7eaf554 2141 * The concept of drawing stops is that a trackpoint
4511d71f
GT
2142 * that is if the next trackpoint has a timestamp far into
2143 * the future, we draw a circle of 6x trackpoint size,
f7eaf554
RN
2144 * instead of a rectangle of 2x trackpoint size.
2145 * This is drawn first so the trackpoint will be drawn on top
4511d71f 2146 */
50a14534 2147 /* stops */
b42a25ba 2148 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
b1453c16
RN
2149 /* Stop point. Draw 6x circle. Always in redish colour */
2150 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
2151
2152 /* Regular point - draw 2x square. */
2153 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
50a14534
EB
2154 }
2155 else
4511d71f 2156 /* Final point - draw 4x circle. */
8e9c992d 2157 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
2158 }
2159
2160 if ((!tp->newsegment) && (dp->vtl->drawlines))
2161 {
50a14534
EB
2162
2163 /* UTM only: zone check */
2164 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
8e9c992d 2165 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
50a14534 2166
50a14534
EB
2167 if (!useoldvals)
2168 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2169
0c9b1cf7 2170 if ( draw_track_outline ) {
50a14534 2171 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
2172 }
2173 else {
8c4f1350 2174
8e9c992d 2175 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
08084371 2176
3b9970f8 2177 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
b42a25ba
EB
2178 GdkPoint tmp[4];
2179 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
3b9970f8
RN
2180
2181 tmp[0].x = oldx;
2182 tmp[0].y = oldy;
2183 tmp[1].x = oldx;
2184 tmp[1].y = oldy-FIXALTITUDE(list->data);
2185 tmp[2].x = x;
2186 tmp[2].y = y-FIXALTITUDE(list->next->data);
2187 tmp[3].x = x;
2188 tmp[3].y = y;
2189
2190 GdkGC *tmp_gc;
2191 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
ff37db21 2192 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
3b9970f8 2193 else
ff37db21 2194 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
3b9970f8
RN
2195 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2196
8e9c992d 2197 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
8c4f1350
EB
2198 }
2199 }
50a14534
EB
2200 }
2201
08084371
RN
2202 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2203 // Draw an arrow at the mid point to show the direction of the track
2204 // Code is a rework from vikwindow::draw_ruler()
2205 gint midx = (oldx + x) / 2;
2206 gint midy = (oldy + y) / 2;
2207
2208 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2209 // Avoid divide by zero and ensure at least 1 pixel big
2210 if ( len > 1 ) {
2211 gdouble dx = (oldx - midx) / len;
2212 gdouble dy = (oldy - midy) / len;
2213 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2214 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2215 }
2216 }
2217
adc271a8 2218 skip:
50a14534
EB
2219 oldx = x;
2220 oldy = y;
2221 useoldvals = TRUE;
2222 }
2223 else {
2224 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2225 {
2226 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2227 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2228 {
2229 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
18df6d42
RN
2230
2231 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
b1453c16 2232 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 2233 }
50a14534 2234
adc271a8
GT
2235 /*
2236 * If points are the same in display coordinates, don't draw.
2237 */
2238 if ( x != oldx || y != oldy )
2239 {
0c9b1cf7 2240 if ( draw_track_outline )
adc271a8
GT
2241 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2242 else
2243 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2244 }
50a14534
EB
2245 }
2246 else
2247 {
adc271a8
GT
2248 /*
2249 * If points are the same in display coordinates, don't draw.
2250 */
2251 if ( x != oldx && y != oldy )
2252 {
2253 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2254 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2255 }
50a14534
EB
2256 }
2257 }
2258 useoldvals = FALSE;
2259 }
2260 }
387ff7ac
RN
2261
2262 // Labels drawn after the trackpoints, so the labels are on top
2263 if ( dp->vtl->track_draw_labels ) {
2264 if ( track->max_number_dist_labels > 0 ) {
2265 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2266 }
2267
2268 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2269 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2270 }
2271 }
50a14534 2272 }
50a14534
EB
2273}
2274
9e212bfc 2275static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
50a14534 2276{
20981fd6
RN
2277 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2278 trw_layer_draw_track ( id, track, dp, FALSE );
2279 }
50a14534
EB
2280}
2281
2282static void cached_pixbuf_free ( CachedPixbuf *cp )
2283{
2284 g_object_unref ( G_OBJECT(cp->pixbuf) );
2285 g_free ( cp->image );
2286}
2287
2288static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2289{
2290 return strcmp ( cp->image, name );
2291}
2292
9e212bfc 2293static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
50a14534
EB
2294{
2295 if ( wp->visible )
51f0884d 2296 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
50a14534
EB
2297 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2298 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2299 {
2300 gint x, y;
2301 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2302
2303 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2304
2305 if ( wp->image && dp->vtl->drawimages )
2306 {
2307 GdkPixbuf *pixbuf = NULL;
2308 GList *l;
2309
2310 if ( dp->vtl->image_alpha == 0)
2311 return;
2312
2313 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2314 if ( l )
2315 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2316 else
2317 {
2318 gchar *image = wp->image;
2319 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2320 if ( ! regularthumb )
2321 {
2322 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2323 image = "\x12\x00"; /* this shouldn't occur naturally. */
2324 }
2325 if ( regularthumb )
2326 {
2327 CachedPixbuf *cp = NULL;
2328 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2329 if ( dp->vtl->image_size == 128 )
2330 cp->pixbuf = regularthumb;
2331 else
2332 {
2333 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2334 g_assert ( cp->pixbuf );
2335 g_object_unref ( G_OBJECT(regularthumb) );
2336 }
2337 cp->image = g_strdup ( image );
2338
2339 /* needed so 'click picture' tool knows how big the pic is; we don't
2340 * store it in cp because they may have been freed already. */
2341 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2342 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2343
2344 g_queue_push_head ( dp->vtl->image_cache, cp );
2345 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2346 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2347
2348 pixbuf = cp->pixbuf;
2349 }
2350 else
2351 {
2352 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2353 }
2354 }
2355 if ( pixbuf )
2356 {
2357 gint w, h;
2358 w = gdk_pixbuf_get_width ( pixbuf );
2359 h = gdk_pixbuf_get_height ( pixbuf );
2360
2361 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2362 {
bc07590a
RN
2363 if ( dp->highlight ) {
2364 // Highlighted - so draw a little border around the chosen one
2365 // single line seems a little weak so draw 2 of them
2366 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2367 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2368 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2369 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2370 }
2371
50a14534
EB
2372 if ( dp->vtl->image_alpha == 255 )
2373 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2374 else
2375 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2376 }
2377 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2378 }
2379 }
2380
98acb9a1
RN
2381 // Draw appropriate symbol - either symbol image or simple types
2382 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2383 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
2384 }
2385 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
2386 switch ( dp->vtl->wp_symbol ) {
2387 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;
2388 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;
2389 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;
2390 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 );
2391 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 2392 default: break;
50a14534
EB
2393 }
2394 }
2395 else {
2396 switch ( dp->vtl->wp_symbol ) {
2397 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;
2398 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;
2399 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;
2400 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 );
2401 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 2402 default: break;
50a14534
EB
2403 }
2404 }
2405
2406 if ( dp->vtl->drawlabels )
2407 {
2408 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
a7f9c01e 2409 gint label_x, label_y;
50a14534 2410 gint width, height;
5a77ae71
RN
2411 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2412
2413 // Could this stored in the waypoint rather than recreating each pass?
63ffa244 2414 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
5a77ae71
RN
2415
2416 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2417 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2418 else
2419 // Fallback if parse failure
2420 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2421
2422 g_free ( wp_label_markup );
5a77ae71 2423
50a14534 2424 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
a7f9c01e 2425 label_x = x - width/2;
98acb9a1
RN
2426 if ( wp->symbol_pixbuf )
2427 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
a7f9c01e
QT
2428 else
2429 label_y = y - dp->vtl->wp_size - height - 2;
2430
480fb7e1 2431 /* if highlight mode on, then draw background text in highlight colour */
bc07590a
RN
2432 if ( dp->highlight )
2433 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2434 else
2435 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
a7f9c01e 2436 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
50a14534
EB
2437 }
2438 }
2439}
2440
aa0665e5
RN
2441static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2442{
2443 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2444 trw_layer_draw_waypoint ( id, wp, dp );
2445 }
2446}
2447
bc07590a 2448static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
50a14534
EB
2449{
2450 static struct DrawingParams dp;
2451 g_assert ( l != NULL );
2452
bc07590a 2453 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
50a14534
EB
2454
2455 if ( l->tracks_visible )
2456 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2457
0d2b891f
RN
2458 if ( l->routes_visible )
2459 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2460
50a14534 2461 if (l->waypoints_visible)
aa0665e5 2462 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
50a14534
EB
2463}
2464
bc07590a
RN
2465static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2466{
21ad165a
RN
2467 // 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
2468 // This may seem slightly inefficient to test each time for every layer
2469 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2470 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2471 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2472 return;
2473 trw_layer_draw_with_highlight ( l, data, FALSE );
bc07590a
RN
2474}
2475
2476void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2477{
2478 // Check the layer for visibility (including all the parents visibilities)
2479 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2480 return;
2481 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2482}
2483
2484/**
2485 * vik_trw_layer_draw_highlight_item:
2486 *
2487 * Only handles a single track or waypoint ATM
2488 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2489 */
2490void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2491{
2492 // Check the layer for visibility (including all the parents visibilities)
2493 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2494 return;
2495
2496 static struct DrawingParams dp;
2497 init_drawing_params ( &dp, vtl, vvp, TRUE );
2498
2499 if ( trk ) {
2500 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2501 if ( draw )
2502 trw_layer_draw_track_cb ( NULL, trk, &dp );
2503 }
2504 if ( vtl->waypoints_visible && wpt ) {
2505 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2506 }
2507}
2508
2509/**
2510 * vik_trw_layer_draw_highlight_item:
2511 *
2512 * Generally for drawing all tracks or routes or waypoints
2513 * trks may be actually routes
2514 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2515 */
2516void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2517{
2518 // Check the layer for visibility (including all the parents visibilities)
2519 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2520 return;
2521
2522 static struct DrawingParams dp;
2523 init_drawing_params ( &dp, vtl, vvp, TRUE );
2524
2525 if ( trks ) {
2526 gboolean is_routes = (trks == vtl->routes);
2527 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2528 if ( draw )
2529 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2530 }
2531
2532 if ( vtl->waypoints_visible && wpts )
2533 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2534}
2535
50a14534
EB
2536static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2537{
2538 int i;
2539 if ( vtl->track_bg_gc )
2540 {
2541 g_object_unref ( vtl->track_bg_gc );
2542 vtl->track_bg_gc = NULL;
2543 }
b1453c16
RN
2544 if ( vtl->track_1color_gc )
2545 {
2546 g_object_unref ( vtl->track_1color_gc );
2547 vtl->track_1color_gc = NULL;
2548 }
8e9c992d
EB
2549 if ( vtl->current_track_gc )
2550 {
2551 g_object_unref ( vtl->current_track_gc );
2552 vtl->current_track_gc = NULL;
2553 }
745fda83
RN
2554 if ( vtl->current_track_newpoint_gc )
2555 {
2556 g_object_unref ( vtl->current_track_newpoint_gc );
2557 vtl->current_track_newpoint_gc = NULL;
2558 }
50a14534
EB
2559
2560 if ( ! vtl->track_gc )
2561 return;
2562 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2563 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2564 g_array_free ( vtl->track_gc, TRUE );
2565 vtl->track_gc = NULL;
2566}
2567
2568static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2569{
2570 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2571 gint width = vtl->line_thickness;
2572
2573 if ( vtl->track_gc )
2574 trw_layer_free_track_gcs ( vtl );
2575
2576 if ( vtl->track_bg_gc )
2577 g_object_unref ( vtl->track_bg_gc );
a7023a1b 2578 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
50a14534 2579
98b7eddc
RN
2580 // Ensure new track drawing heeds line thickness setting
2581 // however always have a minium of 2, as 1 pixel is really narrow
2582 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2583
8e9c992d
EB
2584 if ( vtl->current_track_gc )
2585 g_object_unref ( vtl->current_track_gc );
98b7eddc
RN
2586 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2587 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
8e9c992d 2588
745fda83
RN
2589 // 'newpoint' gc is exactly the same as the current track gc
2590 if ( vtl->current_track_newpoint_gc )
2591 g_object_unref ( vtl->current_track_newpoint_gc );
98b7eddc
RN
2592 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2593 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 2594
50a14534
EB
2595 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2596
b1453c16
RN
2597 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2598 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
50a14534 2599
18df6d42
RN
2600 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2601 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2602 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2603
074b1067
RN
2604 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2605
50a14534
EB
2606 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2607}
2608
a7cd93ac 2609static VikTrwLayer* trw_layer_create ( VikViewport *vp )
50a14534 2610{
a7023a1b 2611 VikTrwLayer *rv = trw_layer_new1 ( vp );
ba8be43f
RN
2612 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2613
9b082b39 2614 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
ba8be43f
RN
2615 /* early exit, as the rest is GUI related */
2616 return rv;
2617 }
2618
50a14534 2619 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
ff37db21 2620 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
50a14534 2621
387ff7ac
RN
2622 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2623 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2624
50a14534
EB
2625 trw_layer_new_track_gcs ( rv, vp );
2626
a7023a1b
RN
2627 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2628 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2629 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2630 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
50a14534
EB
2631
2632 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2633
20c7a3a0
QT
2634 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2635
50a14534
EB
2636 return rv;
2637}
2638
70cefedc
RN
2639#define SMALL_ICON_SIZE 18
2640/*
2641 * Can accept a null symbol, and may return null value
2642 */
d18f2933 2643GdkPixbuf* get_wp_sym_small ( gchar *symbol )
70cefedc
RN
2644{
2645 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2646 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2647 // So needing a small icon for the treeview may need some resizing:
2648 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2649 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2650 return wp_icon;
2651}
2652
ce4bd1cf 2653static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
50a14534
EB
2654{
2655 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2656
b1453c16
RN
2657 GdkPixbuf *pixbuf = NULL;
2658
2659 if ( track->has_color ) {
2660 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2661 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2662 // Here is some magic found to do the conversion
2663 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2664 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2665 ((track->color.green & 0xff00) << 8) |
2666 (track->color.blue & 0xff00);
2667
2668 gdk_pixbuf_fill ( pixbuf, pixel );
2669 }
2670
c9cac058 2671 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 2672
b1453c16
RN
2673 if ( pixbuf )
2674 g_object_unref (pixbuf);
2675
50a14534 2676 *new_iter = *((GtkTreeIter *) pass_along[1]);
0d2b891f
RN
2677 if ( track->is_route )
2678 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2679 else
2680 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
50a14534
EB
2681
2682 if ( ! track->visible )
2683 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2684}
2685
c9570f86 2686static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
50a14534
EB
2687{
2688 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
70cefedc 2689
c9cac058 2690 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
2691
2692 *new_iter = *((GtkTreeIter *) pass_along[1]);
c9570f86 2693 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
50a14534
EB
2694
2695 if ( ! wp->visible )
2696 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2697}
2698
cd3f311e 2699static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
50a14534 2700{
c9cac058 2701 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
cd3f311e 2702}
50a14534 2703
cd3f311e
RN
2704static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2705{
c9cac058 2706 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
cd3f311e 2707}
50a14534 2708
0d2b891f
RN
2709static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2710{
c9cac058 2711 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
0d2b891f
RN
2712}
2713
cd3f311e
RN
2714static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2715{
2716 GtkTreeIter iter2;
2717 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
50a14534 2718
cd3f311e
RN
2719 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2720 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
9414408e 2721
cd3f311e 2722 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
50a14534 2723
c9cac058 2724 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
cd3f311e 2725 }
50a14534 2726
0d2b891f 2727 if ( g_hash_table_size (vtl->routes) > 0 ) {
0d2b891f
RN
2728 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2729
2730 pass_along[0] = &(vtl->routes_iter);
2731 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2732
2733 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2734
2735 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2736 }
2737
cd3f311e 2738 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
0d2b891f 2739 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
cd3f311e
RN
2740
2741 pass_along[0] = &(vtl->waypoints_iter);
2742 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2743
2744 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2745
2746 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2747 }
0d2b891f 2748
50a14534
EB
2749}
2750
a7cd93ac 2751static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
50a14534
EB
2752{
2753 switch ( subtype )
2754 {
2755 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2756 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
0d2b891f 2757 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
50a14534
EB
2758 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2759 {
2760 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2761 if (t)
2762 return (t->visible ^= 1);
2763 else
2764 return TRUE;
2765 }
2766 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2767 {
2768 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2769 if (t)
2770 return (t->visible ^= 1);
2771 else
2772 return TRUE;
2773 }
0d2b891f
RN
2774 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2775 {
2776 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2777 if (t)
2778 return (t->visible ^= 1);
2779 else
2780 return TRUE;
2781 }
9351abc4 2782 default: break;
50a14534
EB
2783 }
2784 return TRUE;
2785}
2786
04f36d92
RN
2787/*
2788 * Return a property about tracks for this layer
2789 */
2790gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2791{
2792 return vtl->line_thickness;
2793}
2794
cb89c5a5
RN
2795// Structure to hold multiple track information for a layer
2796typedef struct {
2797 gdouble length;
2798 time_t start_time;
2799 time_t end_time;
2800 gint duration;
2801} tooltip_tracks;
2802
2803/*
2804 * Build up layer multiple track information via updating the tooltip_tracks structure
2805 */
2806static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2807{
2808 tt->length = tt->length + vik_track_get_length (tr);
2809
2810 // Ensure times are available
a970009c
RN
2811 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2812 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2813 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2814 if ( trkpt_last->has_timestamp ) {
2815 time_t t1, t2;
2816 t1 = vik_track_get_tp_first(tr)->timestamp;
2817 t2 = trkpt_last->timestamp;
2818
2819 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2820 // Hence initialize to the first 'proper' value
2821 if ( tt->start_time == 0 )
2822 tt->start_time = t1;
2823 if ( tt->end_time == 0 )
2824 tt->end_time = t2;
2825
2826 // Update find the earliest / last times
2827 if ( t1 < tt->start_time )
2828 tt->start_time = t1;
2829 if ( t2 > tt->end_time )
2830 tt->end_time = t2;
2831
2832 // Keep track of total time
2833 // there maybe gaps within a track (eg segments)
2834 // but this should be generally good enough for a simple indicator
2835 tt->duration = tt->duration + (int)(t2-t1);
2836 }
cb89c5a5
RN
2837 }
2838}
2839
2840/*
2841 * Generate tooltip text for the layer.
2842 * This is relatively complicated as it considers information for
2843 * no tracks, a single track or multiple tracks
2844 * (which may or may not have timing information)
2845 */
2846static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2847{
2848 gchar tbuf1[32];
2849 gchar tbuf2[64];
2850 gchar tbuf3[64];
2851 gchar tbuf4[10];
2852 tbuf1[0] = '\0';
2853 tbuf2[0] = '\0';
2854 tbuf3[0] = '\0';
2855 tbuf4[0] = '\0';
2856
2857 static gchar tmp_buf[128];
2858 tmp_buf[0] = '\0';
2859
2860 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2861
2862 // Safety check - I think these should always be valid
2863 if ( vtl->tracks && vtl->waypoints ) {
63959706 2864 tooltip_tracks tt = { 0.0, 0, 0, 0 };
cb89c5a5
RN
2865 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2866
2867 GDate* gdate_start = g_date_new ();
2868 g_date_set_time_t (gdate_start, tt.start_time);
2869
2870 GDate* gdate_end = g_date_new ();
2871 g_date_set_time_t (gdate_end, tt.end_time);
2872
2873 if ( g_date_compare (gdate_start, gdate_end) ) {
2874 // Dates differ so print range on separate line
2875 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2876 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2877 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2878 }
2879 else {
2880 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2881 if ( tt.start_time != 0 )
2882 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2883 }
2884
2885 tbuf2[0] = '\0';
2886 if ( tt.length > 0.0 ) {
2887 gdouble len_in_units;
2888
2889 // Setup info dependent on distance units
b22233bd
RN
2890 switch ( a_vik_get_units_distance() ) {
2891 case VIK_UNITS_DISTANCE_MILES:
2892 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2893 len_in_units = VIK_METERS_TO_MILES(tt.length);
2894 break;
2895 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2896 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2897 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2898 break;
2899 default:
2900 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2901 len_in_units = tt.length/1000.0;
2902 break;
cb89c5a5
RN
2903 }
2904
2905 // Timing information if available
2906 tbuf1[0] = '\0';
2907 if ( tt.duration > 0 ) {
2908 g_snprintf (tbuf1, sizeof(tbuf1),
2909 _(" in %d:%02d hrs:mins"),
2910 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2911 }
2912 g_snprintf (tbuf2, sizeof(tbuf2),
2913 _("\n%sTotal Length %.1f %s%s"),
2914 tbuf3, len_in_units, tbuf4, tbuf1);
2915 }
2916
2917 // Put together all the elements to form compact tooltip text
2918 g_snprintf (tmp_buf, sizeof(tmp_buf),
0d2b891f
RN
2919 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2920 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
cb89c5a5
RN
2921
2922 g_date_free (gdate_start);
2923 g_date_free (gdate_end);
2924
2925 }
2926
2927 return tmp_buf;
2928}
2929
c7060c4e
RN
2930static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2931{
2932 switch ( subtype )
2933 {
cc837b7a
RN
2934 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2935 {
2936 // Very simple tooltip - may expand detail in the future...
2937 static gchar tmp_buf[32];
2938 g_snprintf (tmp_buf, sizeof(tmp_buf),
2939 _("Tracks: %d"),
2940 g_hash_table_size (l->tracks));
2941 return tmp_buf;
2942 }
2943 break;
0d2b891f
RN
2944 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2945 {
2946 // Very simple tooltip - may expand detail in the future...
2947 static gchar tmp_buf[32];
2948 g_snprintf (tmp_buf, sizeof(tmp_buf),
2949 _("Routes: %d"),
2950 g_hash_table_size (l->routes));
2951 return tmp_buf;
2952 }
2953 break;
2954
2955 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2956 // Same tooltip for a route
c7060c4e
RN
2957 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2958 {
0d2b891f
RN
2959 VikTrack *tr;
2960 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2961 tr = g_hash_table_lookup ( l->tracks, sublayer );
2962 else
2963 tr = g_hash_table_lookup ( l->routes, sublayer );
2964
c7060c4e
RN
2965 if ( tr ) {
2966 // Could be a better way of handling strings - but this works...
2967 gchar time_buf1[20];
2968 gchar time_buf2[20];
2969 time_buf1[0] = '\0';
2970 time_buf2[0] = '\0';
2971 static gchar tmp_buf[100];
2972 // Compact info: Short date eg (11/20/99), duration and length
2973 // Hopefully these are the things that are most useful and so promoted into the tooltip
215ebe59 2974 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
c7060c4e 2975 // %x The preferred date representation for the current locale without the time.
215ebe59 2976 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
07378116
RN
2977 time_t dur = vik_track_get_duration ( tr );
2978 if ( dur > 0 )
2979 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
c7060c4e
RN
2980 }
2981 // Get length and consider the appropriate distance units
2982 gdouble tr_len = vik_track_get_length(tr);
2983 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2984 switch (dist_units) {
2985 case VIK_UNITS_DISTANCE_KILOMETRES:
2986 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2987 break;
2988 case VIK_UNITS_DISTANCE_MILES:
a492ff13 2989 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
c7060c4e 2990 break;
b22233bd
RN
2991 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2992 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
2993 break;
c7060c4e
RN
2994 default:
2995 break;
2996 }
2997 return tmp_buf;
2998 }
2999 }
3000 break;
cc837b7a
RN
3001 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3002 {
3003 // Very simple tooltip - may expand detail in the future...
3004 static gchar tmp_buf[32];
3005 g_snprintf (tmp_buf, sizeof(tmp_buf),
3006 _("Waypoints: %d"),
3007 g_hash_table_size (l->waypoints));
3008 return tmp_buf;
3009 }
3010 break;
c7060c4e
RN
3011 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3012 {
3013 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
3014 // NB It's OK to return NULL
382b8734
RN
3015 if ( w ) {
3016 if ( w->comment )
3017 return w->comment;
3018 else
3019 return w->description;
3020 }
c7060c4e
RN
3021 }
3022 break;
3023 default: break;
3024 }
3025 return NULL;
3026}
3027
60626a3e
RN
3028#define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
3029
3030/**
3031 * set_statusbar_msg_info_trkpt:
3032 *
3033 * Function to show track point information on the statusbar
3034 * Items displayed is controlled by the settings format code
95d1b757
RN
3035 */
3036static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
3037{
60626a3e
RN
3038 gchar *statusbar_format_code = NULL;
3039 gboolean need2free = FALSE;
fee4e142 3040 VikTrackpoint *trkpt_prev = NULL;
60626a3e
RN
3041 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3042 // Otherwise use default
b45865b4 3043 statusbar_format_code = g_strdup ( "KEATDN" );
60626a3e 3044 need2free = TRUE;
95d1b757 3045 }
f41c73bd
RN
3046 else {
3047 // Format code may want to show speed - so may need previous trkpt to work it out
3048 trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
3049 }
95d1b757 3050
fee4e142 3051 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
95d1b757 3052 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
95d1b757 3053 g_free ( msg );
60626a3e
RN
3054
3055 if ( need2free )
3056 g_free ( statusbar_format_code );
95d1b757
RN
3057}
3058
3059/*
3060 * Function to show basic waypoint information on the statusbar
3061 */
3062static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3063{
3064 gchar tmp_buf1[64];
3065 switch (a_vik_get_units_height ()) {
3066 case VIK_UNITS_HEIGHT_FEET:
3067 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3068 break;
3069 default:
3070 //VIK_UNITS_HEIGHT_METRES:
3071 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3072 }
3073
3074 // Position part
3075 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3076 // one can easily use the current pointer position to see this if needed
3077 gchar *lat = NULL, *lon = NULL;
3078 static struct LatLon ll;
3079 vik_coord_to_latlon (&(wpt->coord), &ll);
3080 a_coords_latlon_to_string ( &ll, &lat, &lon );
3081
3082 // Combine parts to make overall message
3083 gchar *msg;
3084 if ( wpt->comment )
3085 // Add comment if available
3086 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3087 else
3088 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3089 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3090 g_free ( lat );
3091 g_free ( lon );
3092 g_free ( msg );
3093}
3094
a5dcfdb7
RN
3095/**
3096 * General layer selection function, find out which bit is selected and take appropriate action
3097 */
a7cd93ac 3098static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
a5dcfdb7 3099{
c95d6b00 3100 // Reset
c9570f86
RN
3101 l->current_wp = NULL;
3102 l->current_wp_id = NULL;
c95d6b00
RN
3103 trw_layer_cancel_current_tp ( l, FALSE );
3104
95d1b757
RN
3105 // Clear statusbar
3106 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3107
a5dcfdb7
RN
3108 switch ( type )
3109 {
3110 case VIK_TREEVIEW_TYPE_LAYER:
3111 {
3112 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3113 /* Mark for redraw */
3114 return TRUE;
3115 }
3116 break;
3117
3118 case VIK_TREEVIEW_TYPE_SUBLAYER:
3119 {
3120 switch ( subtype )
3121 {
3122 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3123 {
113c74f6 3124 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
a5dcfdb7
RN
3125 /* Mark for redraw */
3126 return TRUE;
3127 }
3128 break;
3129 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3130 {
95d1b757 3131 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
b16effab 3132 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
a5dcfdb7
RN
3133 /* Mark for redraw */
3134 return TRUE;
3135 }
3136 break;
0d2b891f
RN
3137 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3138 {
3139 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3140 /* Mark for redraw */
3141 return TRUE;
3142 }
3143 break;
3144 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3145 {
3146 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3147 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3148 /* Mark for redraw */
3149 return TRUE;
3150 }
3151 break;
a5dcfdb7
RN
3152 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3153 {
113c74f6 3154 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
a5dcfdb7
RN
3155 /* Mark for redraw */
3156 return TRUE;
3157 }
3158 break;
3159 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3160 {
b16effab
RN
3161 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3162 if ( wpt ) {
3163 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3164 // Show some waypoint info
3165 set_statusbar_msg_info_wpt ( l, wpt );
3166 /* Mark for redraw */
3167 return TRUE;
3168 }
a5dcfdb7
RN
3169 }
3170 break;
3171 default:
3172 {
3173 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3174 }
3175 break;
3176 }
3177 return FALSE;
3178 }
3179 break;
3180
3181 default:
3182 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3183 break;
3184 }
3185}
c7060c4e 3186
50a14534
EB
3187GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3188{
3189 return l->tracks;
3190}
3191
0d2b891f
RN
3192GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3193{
3194 return l->routes;
3195}
3196
50a14534
EB
3197GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3198{
3199 return l->waypoints;
3200}
3201
c98e2f73
RN
3202GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3203{
3204 return vtl->tracks_iters;
3205}
3206
3207GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3208{
3209 return vtl->routes_iters;
3210}
3211
3212GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3213{
3214 return vtl->waypoints;
3215}
3216
bec82ff5
RN
3217gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3218{
3219 return ! ( g_hash_table_size ( vtl->tracks ) ||
3220 g_hash_table_size ( vtl->routes ) ||
3221 g_hash_table_size ( vtl->waypoints ) );
3222}
3223
0082c2f6
RN
3224gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3225{
3226 return vtl->tracks_visible;
3227}
3228
3229gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3230{
3231 return vtl->routes_visible;
3232}
3233
3234gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3235{
3236 return vtl->waypoints_visible;
3237}
3238
c9570f86
RN
3239/*
3240 * ATM use a case sensitive find
3241 * Finds the first one
3242 */
3243static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3244{
3245 if ( wp && wp->name )
3246 if ( ! strcmp ( wp->name, name ) )
3247 return TRUE;
3248 return FALSE;
3249}
3250
3251/*
3252 * Get waypoint by name - not guaranteed to be unique
3253 * Finds the first one
3254 */
3255VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3256{
3257 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3258}
3259
ce4bd1cf
RN
3260/*
3261 * ATM use a case sensitive find
3262 * Finds the first one
3263 */
3264static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3265{
3266 if ( trk && trk->name )
3267 if ( ! strcmp ( trk->name, name ) )
3268 return TRUE;
3269 return FALSE;
3270}
3271
3272/*
3273 * Get track by name - not guaranteed to be unique
3274 * Finds the first one
3275 */
c9570f86
RN
3276VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3277{
ce4bd1cf 3278 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
c9570f86
RN
3279}
3280
0d2b891f
RN
3281/*
3282 * Get route by name - not guaranteed to be unique
3283 * Finds the first one
3284 */
3285VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3286{
3287 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3288}
3289
9e212bfc 3290static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
50a14534 3291{
a8e8ffe1
RN
3292 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3293 maxmin[0].lat = trk->bbox.north;
3294 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3295 maxmin[1].lat = trk->bbox.south;
3296 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3297 maxmin[0].lon = trk->bbox.east;
3298 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3299 maxmin[1].lon = trk->bbox.west;
50a14534
EB
3300}
3301
165a4fa9
HR
3302static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3303{
0d2b891f 3304 // Continually reuse maxmin to find the latest maximum and minimum values
a8e8ffe1
RN
3305 // First set to waypoints bounds
3306 maxmin[0].lat = vtl->waypoints_bbox.north;
3307 maxmin[1].lat = vtl->waypoints_bbox.south;
3308 maxmin[0].lon = vtl->waypoints_bbox.east;
3309 maxmin[1].lon = vtl->waypoints_bbox.west;
0d2b891f
RN
3310 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3311 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
165a4fa9 3312}
50a14534
EB
3313
3314gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3315{
3316 /* 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... */
3317 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
165a4fa9 3318 trw_layer_find_maxmin (vtl, maxmin);
50a14534
EB
3319 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3320 return FALSE;
3321 else
3322 {
3323 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3324 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3325 return TRUE;
3326 }
3327}
3328
1c2083ba 3329static void trw_layer_centerize ( menu_array_layer values )
50a14534 3330{
1c2083ba 3331 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 3332 VikCoord coord;
1c2083ba
RN
3333 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3334 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
50a14534 3335 else
1c2083ba 3336 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
3337}
3338
c98e2f73 3339void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
c5638216
RN
3340{
3341 /* First set the center [in case previously viewing from elsewhere] */
3342 /* Then loop through zoom levels until provided positions are in view */
3343 /* This method is not particularly fast - but should work well enough */
3344 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3345 VikCoord coord;
3346 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
be5554c5 3347 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
c5638216
RN
3348
3349 /* Convert into definite 'smallest' and 'largest' positions */
3350 struct LatLon minmin;
3351 if ( maxmin[0].lat < maxmin[1].lat )
3352 minmin.lat = maxmin[0].lat;
3353 else
3354 minmin.lat = maxmin[1].lat;
3355
3356 struct LatLon maxmax;
3357 if ( maxmin[0].lon > maxmin[1].lon )
3358 maxmax.lon = maxmin[0].lon;
3359 else
3360 maxmax.lon = maxmin[1].lon;
3361
3362 /* Never zoom in too far - generally not that useful, as too close ! */
3363 /* Always recalculate the 'best' zoom level */
3364 gdouble zoom = 1.0;
3365 vik_viewport_set_zoom ( vvp, zoom );
3366
3367 gdouble min_lat, max_lat, min_lon, max_lon;
3368 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3369 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3370 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3371 /* NB I think the logic used in this test to determine if the bounds is within view
3372 fails if track goes across 180 degrees longitude.
3373 Hopefully that situation is not too common...
3374 Mind you viking doesn't really do edge locations to well anyway */
3375 if ( min_lat < minmin.lat &&
3376 max_lat > minmin.lat &&
3377 min_lon < maxmax.lon &&
3378 max_lon > maxmax.lon )
3379 /* Found within zoom level */
3380 break;
3381
3382 /* Try next */
3383 zoom = zoom * 2;
3384 vik_viewport_set_zoom ( vvp, zoom );
3385 }
3386}
3387
3388gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3389{
3390 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3391 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3392 trw_layer_find_maxmin (vtl, maxmin);
3393 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3394 return FALSE;
3395 else {
3396 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3397 return TRUE;
3398 }
3399}
3400
1c2083ba 3401static void trw_layer_auto_view ( menu_array_layer values )
5a10c240 3402{
1c2083ba
RN
3403 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3404 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3405 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3406 vik_layers_panel_emit_update ( vlp );
5a10c240
RN
3407 }
3408 else
1c2083ba 3409 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
5a10c240
RN
3410}
3411
e4a11fbe 3412static void trw_layer_export_gpspoint ( menu_array_layer values )
50a14534 3413{
e4a11fbe 3414 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
6e4a49aa 3415
e4a11fbe 3416 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
50a14534 3417
e4a11fbe 3418 g_free ( auto_save_name );
50a14534
EB
3419}
3420
1c2083ba 3421static void trw_layer_export_gpsmapper ( menu_array_layer values )
50a14534 3422{
e4a11fbe
GB
3423 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3424
3425 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3426
3427 g_free ( auto_save_name );
50a14534
EB
3428}
3429
1c2083ba 3430static void trw_layer_export_gpx ( menu_array_layer values )
561e6ad0 3431{
e4a11fbe 3432 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
18d0a1ed 3433
e4a11fbe 3434 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
18d0a1ed
RN
3435
3436 g_free ( auto_save_name );
7f6757c4
RN
3437}
3438
1c2083ba 3439static void trw_layer_export_kml ( menu_array_layer values )
ba9d0a00 3440{
e4a11fbe 3441 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
ba9d0a00 3442
e4a11fbe 3443 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
ba9d0a00
RN
3444
3445 g_free ( auto_save_name );
3446}
3447
1ca082d7
RN
3448static void trw_layer_export_geojson ( menu_array_layer values )
3449{
3450 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3451
3452 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3453
3454 g_free ( auto_save_name );
3455}
3456
17720e63
GB
3457static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3458{
c3c4b41c 3459 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
17720e63
GB
3460 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3461}
ccccf356 3462
1c2083ba 3463static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
ccccf356 3464{
e4a11fbe 3465 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
ccccf356
RN
3466}
3467
1c2083ba 3468static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
ff02058b 3469{
e4a11fbe 3470 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
ff02058b
RN
3471}
3472
19782ffd 3473static void trw_layer_export_gpx_track ( menu_array_sublayer values )
7f6757c4 3474{
19782ffd 3475 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 3476 VikTrack *trk;
19782ffd
RN
3477 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3478 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 3479 else
19782ffd 3480 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf
RN
3481
3482 if ( !trk || !trk->name )
3483 return;
7f6757c4 3484
e4a11fbe 3485 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
7f6757c4 3486
0f704e14 3487 gchar *label = NULL;
19782ffd 3488 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0f704e14
GB
3489 label = _("Export Route as GPX");
3490 else
3491 label = _("Export Track as GPX");
e4a11fbe 3492 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
7f6757c4
RN
3493
3494 g_free ( auto_save_name );
561e6ad0
EB
3495}
3496
d18f2933 3497gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
c9570f86
RN
3498{
3499 wpu_udata *user_data = udata;
3500 if ( wp == user_data->wp ) {
3501 user_data->uuid = id;
3502 return TRUE;
3503 }
3504 return FALSE;
3505}
3506
1c2083ba 3507static void trw_layer_goto_wp ( menu_array_layer values )
50a14534 3508{
1c2083ba
RN
3509 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3510 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
a77d62d8 3511 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1c2083ba 3512 VIK_GTK_WINDOW_FROM_LAYER(vtl),
50a14534
EB
3513 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3514 GTK_STOCK_CANCEL,
3515 GTK_RESPONSE_REJECT,
3516 GTK_STOCK_OK,
3517 GTK_RESPONSE_ACCEPT,
3518 NULL);
3519
3520 GtkWidget *label, *entry;
4c77d5e0 3521 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
3522 entry = gtk_entry_new();
3523
9b082b39
RN
3524 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3525 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
50a14534
EB
3526 gtk_widget_show_all ( label );
3527 gtk_widget_show_all ( entry );
3528
7ea3cb11
RN
3529 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3530
50a14534
EB
3531 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3532 {
c9570f86
RN
3533 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3534 // Find *first* wp with the given name
1c2083ba 3535 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
50a14534 3536
c9570f86 3537 if ( !wp )
1c2083ba 3538 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
50a14534
EB
3539 else
3540 {
be5554c5 3541 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
1c2083ba 3542 vik_layers_panel_emit_update ( vlp );
c9570f86
RN
3543
3544 // Find and select on the side panel
3545 wpu_udata udata;
3546 udata.wp = wp;
3547 udata.uuid = NULL;
3548
3549 // Hmmm, want key of it
1c2083ba 3550 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
c9570f86
RN
3551
3552 if ( wpf && udata.uuid ) {
1c2083ba
RN
3553 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3554 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
c9570f86
RN
3555 }
3556
50a14534
EB
3557 break;
3558 }
3559
c9570f86 3560 g_free ( name );
50a14534
EB
3561
3562 }
3563 gtk_widget_destroy ( dia );
3564}
3565
3566gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3567{
ac1bde8b 3568 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 3569 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
3570 gchar *returned_name;
3571 gboolean updated;
acaf7113 3572 wp->coord = *def_coord;
d60a672e
RN
3573
3574 // Attempt to auto set height if DEM data is available
a90338e5 3575 vik_waypoint_apply_dem_data ( wp, TRUE );
50a14534 3576
d6175f49 3577 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
c9570f86
RN
3578
3579 if ( returned_name )
50a14534 3580 {
805d282e 3581 wp->visible = TRUE;
ac1bde8b
RN
3582 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3583 g_free (default_name);
c9570f86 3584 g_free (returned_name);
50a14534
EB
3585 return TRUE;
3586 }
ac1bde8b 3587 g_free (default_name);
acaf7113 3588 vik_waypoint_free(wp);
50a14534
EB
3589 return FALSE;
3590}
3591
1c2083ba 3592static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
165a4fa9 3593{
165a4fa9 3594 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1c2083ba
RN
3595 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3596 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3597 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3598 VikViewport *vvp = vik_window_viewport(vw);
42d6236f
RN
3599
3600 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3601 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
120ab662 3602 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3603 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3604 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3605}
3606
1c2083ba 3607static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
165a4fa9 3608{
1c2083ba
RN
3609 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3610 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3611 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3612
3613 trw_layer_find_maxmin (vtl, maxmin);
120ab662 3614 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3615 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3616 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3617}
3618
b3eb3b98 3619#ifdef VIK_CONFIG_GEOTAG
19782ffd 3620static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
b3eb3b98 3621{
19782ffd 3622 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3623 if ( wp )
3624 // Update directly - not changing the mtime
3625 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3626}
3627
19782ffd 3628static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
b3eb3b98 3629{
19782ffd 3630 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3631 if ( wp )
3632 // Update directly
3633 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3634}
3635
3636/*
3637 * Use code in separate file for this feature as reasonably complex
3638 */
19782ffd 3639static void trw_layer_geotagging_track ( menu_array_sublayer values )
b3eb3b98 3640{
19782ffd
RN
3641 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3642 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3643 // Unset so can be reverified later if necessary
3644 vtl->has_verified_thumbnails = FALSE;
3645
3646 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3647 vtl,
3648 NULL,
3649 track );
3650}
3651
19782ffd 3652static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
41f4abac 3653{
19782ffd
RN
3654 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3655 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
41f4abac
RN
3656
3657 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3658 vtl,
3659 wpt,
3660 NULL );
b3eb3b98
RN
3661}
3662
1c2083ba 3663static void trw_layer_geotagging ( menu_array_layer values )
b3eb3b98 3664{
1c2083ba 3665 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
b3eb3b98
RN
3666 // Unset so can be reverified later if necessary
3667 vtl->has_verified_thumbnails = FALSE;
3668
3669 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3670 vtl,
3671 NULL,
3672 NULL );
b3eb3b98
RN
3673}
3674#endif
3675
16fc32f6
RN
3676// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3677
1c2083ba 3678static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
16fc32f6 3679{
1c2083ba
RN
3680 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3681 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
16fc32f6
RN
3682 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3683 VikViewport *vvp = vik_window_viewport(vw);
3684
9cc13848
RN
3685 vik_datasource_mode_t mode = datasource->mode;
3686 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3687 mode = VIK_DATASOURCE_ADDTOLAYER;
3688 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
995e9fd9
GB
3689}
3690
3691/*
3692 * Acquire into this TRW Layer straight from GPS Device
3693 */
1c2083ba 3694static void trw_layer_acquire_gps_cb ( menu_array_layer values )
995e9fd9 3695{
1c2083ba 3696 trw_layer_acquire ( values, &vik_datasource_gps_interface );
16fc32f6
RN
3697}
3698
3699/*
7f95fd54 3700 * Acquire into this TRW Layer from Directions
16fc32f6 3701 */
1c2083ba 3702static void trw_layer_acquire_routing_cb ( menu_array_layer values )
16fc32f6 3703{
1c2083ba 3704 trw_layer_acquire ( values, &vik_datasource_routing_interface );
16fc32f6
RN
3705}
3706
c6acf18d
RN
3707/*
3708 * Acquire into this TRW Layer from an entered URL
3709 */
1c2083ba 3710static void trw_layer_acquire_url_cb ( menu_array_layer values )
c6acf18d 3711{
1c2083ba 3712 trw_layer_acquire ( values, &vik_datasource_url_interface );
c6acf18d
RN
3713}
3714
40f5740b
RN
3715#ifdef VIK_CONFIG_OPENSTREETMAP
3716/*
3717 * Acquire into this TRW Layer from OSM
3718 */
1c2083ba 3719static void trw_layer_acquire_osm_cb ( menu_array_layer values )
40f5740b 3720{
1c2083ba 3721 trw_layer_acquire ( values, &vik_datasource_osm_interface );
40f5740b 3722}
3cc57413
RN
3723
3724/**
3725 * Acquire into this TRW Layer from OSM for 'My' Traces
3726 */
1c2083ba 3727static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3cc57413 3728{
1c2083ba 3729 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3cc57413 3730}
40f5740b
RN
3731#endif
3732
16fc32f6
RN
3733#ifdef VIK_CONFIG_GEOCACHES
3734/*
3735 * Acquire into this TRW Layer from Geocaching.com
3736 */
1c2083ba 3737static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
16fc32f6 3738{
1c2083ba 3739 trw_layer_acquire ( values, &vik_datasource_gc_interface );
16fc32f6
RN
3740}
3741#endif
3742
68bab1bd
RN
3743#ifdef VIK_CONFIG_GEOTAG
3744/*
3745 * Acquire into this TRW Layer from images
3746 */
1c2083ba 3747static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
68bab1bd 3748{
1c2083ba 3749 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
68bab1bd 3750
1c2083ba 3751 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
68bab1bd
RN
3752
3753 // Reverify thumbnails as they may have changed
3754 vtl->has_verified_thumbnails = FALSE;
3755 trw_layer_verify_thumbnails ( vtl, NULL );
3756}
3757#endif
3758
9cc13848
RN
3759/*
3760 * Acquire into this TRW Layer from any GPS Babel supported file
3761 */
3762static void trw_layer_acquire_file_cb ( menu_array_layer values )
3763{
3764 trw_layer_acquire ( values, &vik_datasource_file_interface );
3765}
3766
1c2083ba 3767static void trw_layer_gps_upload ( menu_array_layer values )
e50758c7 3768{
19782ffd
RN
3769 menu_array_sublayer data;
3770 gint ii;
3771 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3772 data[ii] = NULL;
3773 data[MA_VTL] = values[MA_VTL];
3774 data[MA_VLP] = values[MA_VLP];
e50758c7 3775
19782ffd 3776 trw_layer_gps_upload_any ( data );
e50758c7
RN
3777}
3778
3779/**
3780 * If pass_along[3] is defined that this will upload just that track
3781 */
19782ffd 3782static void trw_layer_gps_upload_any ( menu_array_sublayer values )
e50758c7 3783{
19782ffd
RN
3784 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3785 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
e50758c7 3786
19782ffd 3787 // May not actually get a track here as values[2&3] can be null
0d2b891f
RN
3788 VikTrack *track = NULL;
3789 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3790 gboolean xfer_all = FALSE;
e50758c7 3791
19782ffd 3792 if ( values[MA_SUBTYPE] ) {
0d2b891f 3793 xfer_all = FALSE;
19782ffd
RN
3794 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3795 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3796 xfer_type = RTE;
3797 }
19782ffd
RN
3798 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3799 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3800 xfer_type = TRK;
3801 }
19782ffd 3802 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
0d2b891f
RN
3803 xfer_type = WPT;
3804 }
19782ffd 3805 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d2b891f
RN
3806 xfer_type = RTE;
3807 }
3808 }
19782ffd 3809 else if ( !values[MA_CONFIRM] )
0d2b891f 3810 xfer_all = TRUE; // i.e. whole layer
e50758c7 3811
0d2b891f 3812 if (track && !track->visible) {
e50758c7
RN
3813 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3814 return;
3815 }
3816
3817 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
19782ffd 3818 VIK_GTK_WINDOW_FROM_LAYER(vtl),
e50758c7
RN
3819 GTK_DIALOG_DESTROY_WITH_PARENT,
3820 GTK_STOCK_OK,
3821 GTK_RESPONSE_ACCEPT,
3822 GTK_STOCK_CANCEL,
3823 GTK_RESPONSE_REJECT,
3824 NULL );
3825
3826 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3827 GtkWidget *response_w = NULL;
3828#if GTK_CHECK_VERSION (2, 20, 0)
3829 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3830#endif
3831
3832 if ( response_w )
3833 gtk_widget_grab_focus ( response_w );
3834
0d2b891f 3835 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
e50758c7
RN
3836
3837 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3838 datasource_gps_clean_up ( dgs );
3839 gtk_widget_destroy ( dialog );
3840 return;
3841 }
3842
3843 // Get info from reused datasource dialog widgets
3844 gchar* protocol = datasource_gps_get_protocol ( dgs );
3845 gchar* port = datasource_gps_get_descriptor ( dgs );
3846 // NB don't free the above strings as they're references to values held elsewhere
3847 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
0d2b891f 3848 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
e50758c7
RN
3849 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3850 gboolean turn_off = datasource_gps_get_off ( dgs );
3851
3852 gtk_widget_destroy ( dialog );
3853
3854 // When called from the viewport - work the corresponding layerspanel:
3855 if ( !vlp ) {
3856 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3857 }
3858
3859 // Apply settings to transfer to the GPS device
3860 vik_gps_comm ( vtl,
3861 track,
3862 GPS_UP,
3863 protocol,
3864 port,
3865 FALSE,
3866 vik_layers_panel_get_viewport (vlp),
3867 vlp,
3868 do_tracks,
0d2b891f 3869 do_routes,
e50758c7
RN
3870 do_waypoints,
3871 turn_off );
3872}
3873
1c2083ba 3874static void trw_layer_new_wp ( menu_array_layer values )
50a14534 3875{
1c2083ba
RN
3876 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3877 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
50a14534
EB
3878 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3879 instead return true if you want to update. */
aa0665e5
RN
3880 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 ) {
3881 trw_layer_calculate_bounds_waypoints ( vtl );
50a14534 3882 vik_layers_panel_emit_update ( vlp );
aa0665e5 3883 }
50a14534
EB
3884}
3885
2f154c90
RN
3886static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3887{
3888 vtl->current_track = vik_track_new();
387ff7ac 3889 vik_track_set_defaults ( vtl->current_track );
2f154c90
RN
3890 vtl->current_track->visible = TRUE;
3891 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3892 // Create track with the preferred colour from the layer properties
3893 vtl->current_track->color = vtl->track_color;
3894 else
3895 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3896 vtl->current_track->has_color = TRUE;
3897 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3898}
3899
1c2083ba 3900static void trw_layer_new_track ( menu_array_layer values )
37615c52 3901{
1c2083ba 3902 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52
RN
3903
3904 if ( ! vtl->current_track ) {
3905 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
2f154c90 3906 new_track_create_common ( vtl, name );
1613e468 3907 g_free ( name );
37615c52
RN
3908
3909 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3910 }
3911}
3912
b1453c16
RN
3913static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3914{
3915 vtl->current_track = vik_track_new();
387ff7ac 3916 vik_track_set_defaults ( vtl->current_track );
b1453c16
RN
3917 vtl->current_track->visible = TRUE;
3918 vtl->current_track->is_route = TRUE;
3919 // By default make all routes red
3920 vtl->current_track->has_color = TRUE;
3921 gdk_color_parse ( "red", &vtl->current_track->color );
3922 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3923}
3924
1c2083ba 3925static void trw_layer_new_route ( menu_array_layer values )
0d2b891f 3926{
1c2083ba 3927 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
3928
3929 if ( ! vtl->current_track ) {
3930 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
b1453c16 3931 new_route_create_common ( vtl, name );
1613e468 3932 g_free ( name );
e37b2a6d 3933 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
0d2b891f
RN
3934 }
3935}
3936
1c2083ba 3937static void trw_layer_auto_routes_view ( menu_array_layer values )
0d2b891f 3938{
1c2083ba
RN
3939 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3940 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
0d2b891f
RN
3941
3942 if ( g_hash_table_size (vtl->routes) > 0 ) {
3943 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3944 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3945 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3946 vik_layers_panel_emit_update ( vlp );
3947 }
3948}
3949
3950
1c2083ba 3951static void trw_layer_finish_track ( menu_array_layer values )
37615c52 3952{
1c2083ba 3953 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52 3954 vtl->current_track = NULL;
ee43b101 3955 vtl->route_finder_started = FALSE;
da121f9b 3956 vik_layer_emit_update ( VIK_LAYER(vtl) );
37615c52
RN
3957}
3958
1c2083ba 3959static void trw_layer_auto_tracks_view ( menu_array_layer values )
535ed1ae 3960{
1c2083ba
RN
3961 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3962 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
535ed1ae
RN
3963
3964 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3965 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3966 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3967 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3968 vik_layers_panel_emit_update ( vlp );
3969 }
3970}
3971
9e212bfc 3972static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
fc59e8c7
RN
3973{
3974 /* NB do not care if wp is visible or not */
be5554c5 3975 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
fc59e8c7
RN
3976}
3977
1c2083ba 3978static void trw_layer_auto_waypoints_view ( menu_array_layer values )
fc59e8c7 3979{
1c2083ba
RN
3980 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3981 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
fc59e8c7
RN
3982
3983 /* Only 1 waypoint - jump straight to it */
3984 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3985 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3986 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3987 }
3988 /* If at least 2 waypoints - find center and then zoom to fit */
3989 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3990 {
3991 struct LatLon maxmin[2] = { {0,0}, {0,0} };
a8e8ffe1
RN
3992 maxmin[0].lat = vtl->waypoints_bbox.north;
3993 maxmin[1].lat = vtl->waypoints_bbox.south;
3994 maxmin[0].lon = vtl->waypoints_bbox.east;
3995 maxmin[1].lon = vtl->waypoints_bbox.west;
fc59e8c7
RN
3996 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3997 }
3998
3999 vik_layers_panel_emit_update ( vlp );
4000}
4001
1c2083ba 4002void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3185b5d3 4003{
1c2083ba 4004 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3185b5d3
RN
4005}
4006
d6c110f1 4007void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3185b5d3 4008{
d6c110f1
RN
4009 if ( values[MA_MISC] ) {
4010 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
4011 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3185b5d3
RN
4012 }
4013}
4014
a7cd93ac 4015static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
50a14534 4016{
1c2083ba 4017 static menu_array_layer pass_along;
50a14534 4018 GtkWidget *item;
98fcbbdb 4019 GtkWidget *export_submenu;
1c2083ba
RN
4020 pass_along[MA_VTL] = vtl;
4021 pass_along[MA_VLP] = vlp;
50a14534
EB
4022
4023 item = gtk_menu_item_new();
4024 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4025 gtk_widget_show ( item );
4026
37615c52 4027 if ( vtl->current_track ) {
0d2b891f
RN
4028 if ( vtl->current_track->is_route )
4029 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
4030 else
4031 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
37615c52
RN
4032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
4033 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4034 gtk_widget_show ( item );
4035
4036 // Add separator
4037 item = gtk_menu_item_new ();
4038 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4039 gtk_widget_show ( item );
4040 }
4041
d6de71f9
RN
4042 /* Now with icons */
4043 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4044 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5a10c240
RN
4045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4046 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4047 gtk_widget_show ( item );
4048
48d28f21
RN
4049 GtkWidget *view_submenu = gtk_menu_new();
4050 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4051 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
535ed1ae
RN
4052 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4053 gtk_widget_show ( item );
48d28f21 4054 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
535ed1ae 4055
48d28f21
RN
4056 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4058 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4059 gtk_widget_show ( item );
4060
0d2b891f
RN
4061 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4063 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4064 gtk_widget_show ( item );
4065
48d28f21 4066 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
fc59e8c7 4067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
48d28f21 4068 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
fc59e8c7
RN
4069 gtk_widget_show ( item );
4070
d6de71f9
RN
4071 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4074 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4075 gtk_widget_show ( item );
4076
7306a492 4077 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
50a14534
EB
4078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4079 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4080 gtk_widget_show ( item );
4081
1bd88e66 4082 export_submenu = gtk_menu_new ();
d6de71f9
RN
4083 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
50a14534
EB
4085 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4086 gtk_widget_show ( item );
98fcbbdb 4087 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 4088
7306a492 4089 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
1bd88e66
GB
4090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4091 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4092 gtk_widget_show ( item );
50a14534 4093
7306a492 4094 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
50a14534 4095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 4096 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
4097 gtk_widget_show ( item );
4098
7306a492 4099 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
561e6ad0 4100 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 4101 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
4102 gtk_widget_show ( item );
4103
ba9d0a00
RN
4104 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4106 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4107 gtk_widget_show ( item );
4108
1ca082d7
RN
4109 if ( have_geojson_export ) {
4110 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4111 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4112 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4113 gtk_widget_show ( item );
4114 }
4115
17720e63
GB
4116 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4118 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4119 gtk_widget_show ( item );
4120
6e4bf640 4121 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
ccccf356
RN
4122 item = gtk_menu_item_new_with_mnemonic ( external1 );
4123 g_free ( external1 );
4124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4125 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4126 gtk_widget_show ( item );
4127
6e4bf640 4128 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
ff02058b
RN
4129 item = gtk_menu_item_new_with_mnemonic ( external2 );
4130 g_free ( external2 );
4131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4132 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4133 gtk_widget_show ( item );
4134
e7a8a2f4
RN
4135 GtkWidget *new_submenu = gtk_menu_new();
4136 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4138 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4139 gtk_widget_show(item);
4140 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4141
4142 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
d6de71f9 4143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
50a14534 4144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
e7a8a2f4 4145 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
50a14534 4146 gtk_widget_show ( item );
3e7553ae 4147
37615c52
RN
4148 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4151 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4152 gtk_widget_show ( item );
4153 // Make it available only when a new track *not* already in progress
4154 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4155
0d2b891f
RN
4156 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4159 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4160 gtk_widget_show ( item );
4161 // Make it available only when a new track *not* already in progress
4162 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4163
b3eb3b98
RN
4164#ifdef VIK_CONFIG_GEOTAG
4165 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4167 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4168 gtk_widget_show ( item );
4169#endif
4170
16fc32f6 4171 GtkWidget *acquire_submenu = gtk_menu_new ();
aaecf368 4172 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
d6de71f9 4173 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
16fc32f6
RN
4174 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4175 gtk_widget_show ( item );
4176 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4177
4178 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4180 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4181 gtk_widget_show ( item );
4182
7f95fd54
GB
4183 /* FIXME: only add menu when at least a routing engine has support for Directions */
4184 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
16fc32f6
RN
4186 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4187 gtk_widget_show ( item );
4188
40f5740b
RN
4189#ifdef VIK_CONFIG_OPENSTREETMAP
4190 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4192 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4193 gtk_widget_show ( item );
3cc57413
RN
4194
4195 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4197 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4198 gtk_widget_show ( item );
40f5740b
RN
4199#endif
4200
c6acf18d
RN
4201 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4203 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4204 gtk_widget_show ( item );
4205
aaecf368
RN
4206#ifdef VIK_CONFIG_GEONAMES
4207 GtkWidget *wikipedia_submenu = gtk_menu_new();
4208 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4209 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4210 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4211 gtk_widget_show(item);
4212 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4213
4214 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4215 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4216 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4217 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4218 gtk_widget_show ( item );
4219
4220 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4223 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4224 gtk_widget_show ( item );
4225#endif
4226
16fc32f6
RN
4227#ifdef VIK_CONFIG_GEOCACHES
4228 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4229 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4230 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4231 gtk_widget_show ( item );
4232#endif
4233
68bab1bd
RN
4234#ifdef VIK_CONFIG_GEOTAG
4235 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4237 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4238 gtk_widget_show ( item );
4239#endif
4240
d7ac7564
RN
4241 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4243 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
49f6b9d3 4244 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
d7ac7564
RN
4245 gtk_widget_show ( item );
4246
82993cc7
RN
4247 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4248
e50758c7
RN
4249 GtkWidget *upload_submenu = gtk_menu_new ();
4250 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4251 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4252 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4253 gtk_widget_show ( item );
4254 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4255
0d2b891f
RN
4256 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4257 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4259 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4260 gtk_widget_show ( item );
4261
3e7553ae 4262#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9
RN
4263 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4264 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 4265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
e50758c7 4266 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3e7553ae
GB
4267 gtk_widget_show ( item );
4268#endif
28c82d8b 4269
c9a5cbf9 4270 GtkWidget *delete_submenu = gtk_menu_new ();
d6de71f9
RN
4271 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4272 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4273 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4274 gtk_widget_show ( item );
4275 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4276
d6de71f9
RN
4277 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4278 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4280 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4281 gtk_widget_show ( item );
4282
d6de71f9
RN
4283 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4284 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4285 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4286 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4287 gtk_widget_show ( item );
58517f22
RN
4288
4289 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4290 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4292 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4293 gtk_widget_show ( item );
4294
4295 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4296 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4297 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4298 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4299 gtk_widget_show ( item );
20b671c3 4300
d6de71f9
RN
4301 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4302 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4304 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4305 gtk_widget_show ( item );
4306
d6de71f9
RN
4307 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4308 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4309 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4310 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4311 gtk_widget_show ( item );
4312
28c82d8b 4313 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4314 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4315 if ( item ) {
4316 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4317 gtk_widget_show ( item );
4318 }
4319
4320 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4321 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4322 if ( item ) {
4323 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4324 gtk_widget_show ( item );
07c9d42b
RN
4325 }
4326
4327 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4328 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4330 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4331 gtk_widget_show ( item );
4332 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
00176e85
RN
4333
4334 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4335 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4338 gtk_widget_show ( item );
4339 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
50a14534
EB
4340}
4341
c9570f86
RN
4342// Fake Waypoint UUIDs vi simple increasing integer
4343static guint wp_uuid = 0;
4344
50a14534
EB
4345void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4346{
c9570f86
RN
4347 wp_uuid++;
4348
4349 vik_waypoint_set_name (wp, name);
4350
50a14534
EB
4351 if ( VIK_LAYER(vtl)->realized )
4352 {
cd3f311e
RN
4353 // Do we need to create the sublayer:
4354 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4355 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4356 }
4357
c9570f86
RN
4358 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4359
4360 // Visibility column always needed for waypoints
c9cac058 4361 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 4362
c9570f86
RN
4363 // Actual setting of visibility dependent on the waypoint
4364 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4365
4366 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
7a52aac6
RN
4367
4368 // Sort now as post_read is not called on a realized waypoint
4369 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
50a14534 4370 }
50a14534 4371
a8fe53f8 4372 highest_wp_number_add_wp(vtl, name);
c9570f86 4373 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
50a14534
EB
4374
4375}
4376
ce4bd1cf
RN
4377// Fake Track UUIDs vi simple increasing integer
4378static guint tr_uuid = 0;
4379
50a14534
EB
4380void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4381{
ce4bd1cf
RN
4382 tr_uuid++;
4383
4384 vik_track_set_name (t, name);
4385
50a14534
EB
4386 if ( VIK_LAYER(vtl)->realized )
4387 {
cd3f311e
RN
4388 // Do we need to create the sublayer:
4389 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4390 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4391 }
4392
ce4bd1cf
RN
4393 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4394 // Visibility column always needed for tracks
c9cac058 4395 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 4396
ce4bd1cf
RN
4397 // Actual setting of visibility dependent on the track
4398 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4399
4400 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
c9cac058
RN
4401
4402 // Sort now as post_read is not called on a realized track
4403 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
50a14534 4404 }
50a14534 4405
ce4bd1cf 4406 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
b1453c16 4407
93ee73b3 4408 trw_layer_update_treeview ( vtl, t );
50a14534
EB
4409}
4410
0d2b891f
RN
4411// Fake Route UUIDs vi simple increasing integer
4412static guint rt_uuid = 0;
4413
4414void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4415{
4416 rt_uuid++;
4417
4418 vik_track_set_name (t, name);
4419
4420 if ( VIK_LAYER(vtl)->realized )
4421 {
4422 // Do we need to create the sublayer:
4423 if ( g_hash_table_size (vtl->routes) == 0 ) {
4424 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4425 }
4426
4427 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
c9cac058
RN
4428 // Visibility column always needed for routes
4429 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 );
4430 // Actual setting of visibility dependent on the route
0d2b891f
RN
4431 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4432
4433 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
c9cac058
RN
4434
4435 // Sort now as post_read is not called on a realized route
4436 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
0d2b891f
RN
4437 }
4438
4439 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4440
93ee73b3 4441 trw_layer_update_treeview ( vtl, t );
0d2b891f
RN
4442}
4443
50a14534 4444/* to be called whenever a track has been deleted or may have been changed. */
ce4bd1cf 4445void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4446{
ce4bd1cf 4447 if (vtl->current_tp_track == trk )
50a14534 4448 trw_layer_cancel_current_tp ( vtl, FALSE );
50a14534 4449}
0d2b891f 4450
98acb9a1
RN
4451/**
4452 * Normally this is done to due the waypoint size preference having changed
4453 */
4454void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4455{
4456 GHashTableIter iter;
4457 gpointer key, value;
4458
4459 // Foreach waypoint
4460 g_hash_table_iter_init ( &iter, vtl->waypoints );
4461 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4462 VikWaypoint *wp = VIK_WAYPOINT(value);
4463 if ( wp->symbol ) {
4464 // Reapply symbol setting to update the pixbuf
4465 gchar *tmp_symbol = g_strdup ( wp->symbol );
4466 vik_waypoint_set_symbol ( wp, tmp_symbol );
4467 g_free ( tmp_symbol );
4468 }
4469 }
4470}
4471
1613e468
RN
4472/**
4473 * trw_layer_new_unique_sublayer_name:
4474 *
4475 * Allocates a unique new name
4476 */
9748531a 4477gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
e890a6e6 4478{
0d2b891f
RN
4479 gint i = 2;
4480 gchar *newname = g_strdup(name);
4481
4482 gpointer id = NULL;
4483 do {
4484 id = NULL;
4485 switch ( sublayer_type ) {
4486 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4487 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4488 break;
4489 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4490 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4491 break;
4492 default:
4493 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4494 break;
4495 }
4496 // If found a name already in use try adding 1 to it and we try again
4497 if ( id ) {
4498 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4499 g_free(newname);
4500 newname = new_newname;
4501 i++;
4502 }
4503 } while ( id != NULL);
4504
e890a6e6
EB
4505 return newname;
4506}
50a14534 4507
805d282e
EB
4508void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4509{
c9570f86
RN
4510 // No more uniqueness of name forced when loading from a file
4511 // This now makes this function a little redunant as we just flow the parameters through
4512 vik_trw_layer_add_waypoint ( vtl, name, wp );
805d282e 4513}
c9570f86 4514
805d282e
EB
4515void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4516{
ee43b101 4517 if ( vtl->route_finder_append && vtl->current_track ) {
c3deba01 4518 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ee43b101
MH
4519
4520 // enforce end of current track equal to start of tr
4521 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4522 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4523 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4524 vik_track_add_trackpoint ( vtl->current_track,
4525 vik_trackpoint_copy ( cur_end ),
4526 FALSE );
4527 }
4528
4529 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
bddd2056 4530 vik_track_free ( tr );
7ff7d728 4531 vtl->route_finder_append = FALSE; /* this means we have added it */
bddd2056 4532 } else {
ce4bd1cf
RN
4533
4534 // No more uniqueness of name forced when loading from a file
0d2b891f
RN
4535 if ( tr->is_route )
4536 vik_trw_layer_add_route ( vtl, name, tr );
4537 else
4538 vik_trw_layer_add_track ( vtl, name, tr );
bddd2056 4539
7ff7d728 4540 if ( vtl->route_finder_check_added_track ) {
c3deba01 4541 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ce4bd1cf 4542 vtl->route_finder_added_track = tr;
bddd2056
EB
4543 }
4544 }
805d282e
EB
4545}
4546
ce4bd1cf 4547static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
c48517ad 4548{
ce4bd1cf 4549 *l = g_list_append(*l, id);
c48517ad
AF
4550}
4551
ce4bd1cf
RN
4552/*
4553 * Move an item from one TRW layer to another TRW layer
4554 */
4555static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
c48517ad 4556{
c9cac058 4557 // TODO reconsider strategy when moving within layer (if anything...)
762cb457 4558 gboolean rename = ( vtl_src != vtl_dest );
c9cac058
RN
4559 if ( ! rename )
4560 return;
762cb457 4561
c48517ad 4562 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
ce4bd1cf
RN
4563 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4564
762cb457
RN
4565 gchar *newname;
4566 if ( rename )
4567 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4568 else
4569 newname = g_strdup ( trk->name );
ce4bd1cf 4570
03817fbf 4571 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
ce4bd1cf 4572 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
1613e468 4573 g_free ( newname );
ce4bd1cf 4574 vik_trw_layer_delete_track ( vtl_src, trk );
c48517ad 4575 }
c9570f86 4576
0d2b891f
RN
4577 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4578 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4579
762cb457
RN
4580 gchar *newname;
4581 if ( rename )
4582 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4583 else
4584 newname = g_strdup ( trk->name );
0d2b891f 4585
03817fbf 4586 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
0d2b891f 4587 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
1613e468 4588 g_free ( newname );
0d2b891f
RN
4589 vik_trw_layer_delete_route ( vtl_src, trk );
4590 }
4591
ce4bd1cf
RN
4592 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4593 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
c9570f86 4594
762cb457
RN
4595 gchar *newname;
4596 if ( rename )
4597 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4598 else
4599 newname = g_strdup ( wp->name );
c9570f86 4600
ce4bd1cf
RN
4601 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4602 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
1613e468 4603 g_free ( newname );
c9570f86 4604 trw_layer_delete_waypoint ( vtl_src, wp );
aa0665e5 4605
26e66078
RN
4606 // Recalculate bounds even if not renamed as maybe dragged between layers
4607 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4608 trw_layer_calculate_bounds_waypoints ( vtl_src );
c48517ad
AF
4609 }
4610}
4611
70a23263 4612static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
4613{
4614 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
4615 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4616
e4afc73a 4617 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
4618 GList *items = NULL;
4619 GList *iter;
e4afc73a 4620
c48517ad
AF
4621 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4622 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4623 }
4624 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4625 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4626 }
0d2b891f
RN
4627 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4628 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4629 }
4630
c48517ad
AF
4631 iter = items;
4632 while (iter) {
4633 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
0d2b891f 4634 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
c9cac058 4635 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
0d2b891f 4636 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
c48517ad 4637 } else {
0d2b891f 4638 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
c48517ad
AF
4639 }
4640 iter = iter->next;
e4afc73a 4641 }
c48517ad 4642 if (items)
b637058e 4643 g_list_free(items);
c48517ad
AF
4644 } else {
4645 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4646 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
4647 }
4648}
4649
c98e2f73 4650gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
ce4bd1cf
RN
4651{
4652 trku_udata *user_data = udata;
4653 if ( trk == user_data->trk ) {
4654 user_data->uuid = id;
4655 return TRUE;
4656 }
4657 return FALSE;
4658}
4659
4660gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4661{
50a14534 4662 gboolean was_visible = FALSE;
ce4bd1cf
RN
4663 if ( trk && trk->name ) {
4664
4665 if ( trk == vtl->current_track ) {
50a14534 4666 vtl->current_track = NULL;
ce4bd1cf
RN
4667 vtl->current_tp_track = NULL;
4668 vtl->current_tp_id = NULL;
4669 vtl->moving_tp = FALSE;
ee43b101 4670 vtl->route_finder_started = FALSE;
77ad64fa 4671 }
ce4bd1cf
RN
4672
4673 was_visible = trk->visible;
4674
ce4bd1cf
RN
4675 if ( trk == vtl->route_finder_added_track )
4676 vtl->route_finder_added_track = NULL;
4677
4678 trku_udata udata;
4679 udata.trk = trk;
4680 udata.uuid = NULL;
50a14534 4681
ce4bd1cf
RN
4682 // Hmmm, want key of it
4683 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
50a14534 4684
ce4bd1cf
RN
4685 if ( trkf && udata.uuid ) {
4686 /* could be current_tp, so we have to check */
4687 trw_layer_cancel_tps_of_track ( vtl, trk );
4688
4689 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4690
4691 if ( it ) {
4692 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4693 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4694 g_hash_table_remove ( vtl->tracks, udata.uuid );
cd3f311e
RN
4695
4696 // If last sublayer, then remove sublayer container
4697 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4698 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4699 }
ce4bd1cf 4700 }
bc07590a
RN
4701 // Incase it was selected (no item delete signal ATM)
4702 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
ce4bd1cf 4703 }
50a14534
EB
4704 }
4705 return was_visible;
4706}
4707
0d2b891f
RN
4708gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4709{
4710 gboolean was_visible = FALSE;
4711
4712 if ( trk && trk->name ) {
4713
4714 if ( trk == vtl->current_track ) {
4715 vtl->current_track = NULL;
4716 vtl->current_tp_track = NULL;
4717 vtl->current_tp_id = NULL;
4718 vtl->moving_tp = FALSE;
4719 }
4720
4721 was_visible = trk->visible;
4722
0d2b891f
RN
4723 if ( trk == vtl->route_finder_added_track )
4724 vtl->route_finder_added_track = NULL;
4725
4726 trku_udata udata;
4727 udata.trk = trk;
4728 udata.uuid = NULL;
4729
4730 // Hmmm, want key of it
4731 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4732
4733 if ( trkf && udata.uuid ) {
4734 /* could be current_tp, so we have to check */
4735 trw_layer_cancel_tps_of_track ( vtl, trk );
4736
4737 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4738
4739 if ( it ) {
4740 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4741 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4742 g_hash_table_remove ( vtl->routes, udata.uuid );
4743
4744 // If last sublayer, then remove sublayer container
4745 if ( g_hash_table_size (vtl->routes) == 0 ) {
4746 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4747 }
4748 }
bc07590a
RN
4749 // Incase it was selected (no item delete signal ATM)
4750 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
0d2b891f
RN
4751 }
4752 }
4753 return was_visible;
4754}
4755
c9570f86 4756static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
e4afc73a
EB
4757{
4758 gboolean was_visible = FALSE;
e4afc73a 4759
c9570f86 4760 if ( wp && wp->name ) {
e4afc73a
EB
4761
4762 if ( wp == vtl->current_wp ) {
4763 vtl->current_wp = NULL;
c9570f86 4764 vtl->current_wp_id = NULL;
e4afc73a
EB
4765 vtl->moving_wp = FALSE;
4766 }
4767
4768 was_visible = wp->visible;
c9570f86
RN
4769
4770 wpu_udata udata;
4771 udata.wp = wp;
4772 udata.uuid = NULL;
4773
4774 // Hmmm, want key of it
4775 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4776
4777 if ( wpf && udata.uuid ) {
4778 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4779
4780 if ( it ) {
4781 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4782 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4783
4784 highest_wp_number_remove_wp(vtl, wp->name);
4785 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
cd3f311e
RN
4786
4787 // If last sublayer, then remove sublayer container
4788 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4789 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4790 }
c9570f86 4791 }
bc07590a
RN
4792 // Incase it was selected (no item delete signal ATM)
4793 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
c9570f86 4794 }
a8fe53f8 4795
e4afc73a
EB
4796 }
4797
4798 return was_visible;
4799}
4800
c9570f86
RN
4801// Only for temporary use by trw_layer_delete_waypoint_by_name
4802static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4803{
4804 wpu_udata *user_data = udata;
4805 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4806 user_data->uuid = id;
4807 return TRUE;
4808 }
4809 return FALSE;
4810}
4811
4812/*
4813 * Delete a waypoint by the given name
4814 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4815 * as there be multiple waypoints with the same name
4816 */
4817static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4818{
4819 wpu_udata udata;
4820 // Fake a waypoint with the given name
4821 udata.wp = vik_waypoint_new ();
4822 vik_waypoint_set_name (udata.wp, name);
4823 // Currently only the name is used in this waypoint find function
4824 udata.uuid = NULL;
4825
4826 // Hmmm, want key of it
4827 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4828
4829 vik_waypoint_free (udata.wp);
4830
4831 if ( wpf && udata.uuid )
4832 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4833 else
4834 return FALSE;
4835}
4836
ce4bd1cf
RN
4837typedef struct {
4838 VikTrack *trk; // input
4839 gpointer uuid; // output
4840} tpu_udata;
4841
4842// Only for temporary use by trw_layer_delete_track_by_name
4843static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4844{
4845 tpu_udata *user_data = udata;
4846 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4847 user_data->uuid = id;
4848 return TRUE;
4849 }
4850 return FALSE;
4851}
4852
4853/*
4854 * Delete a track by the given name
4855 * NOTE: ATM this will delete the first encountered Track with the specified name
0d2b891f 4856 * as there may be multiple tracks with the same name within the specified hash table
ce4bd1cf 4857 */
0d2b891f 4858static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
ce4bd1cf
RN
4859{
4860 tpu_udata udata;
4861 // Fake a track with the given name
4862 udata.trk = vik_track_new ();
4863 vik_track_set_name (udata.trk, name);
4864 // Currently only the name is used in this waypoint find function
4865 udata.uuid = NULL;
4866
4867 // Hmmm, want key of it
0d2b891f 4868 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
ce4bd1cf
RN
4869
4870 vik_track_free (udata.trk);
4871
0d2b891f
RN
4872 if ( trkf && udata.uuid ) {
4873 // This could be a little better written...
4874 if ( vtl->tracks == ht_tracks )
4875 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4876 if ( vtl->routes == ht_tracks )
4877 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4878 return FALSE;
4879 }
ce4bd1cf
RN
4880 else
4881 return FALSE;
4882}
4883
9e212bfc 4884static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
700b0908
QT
4885{
4886 vik_treeview_item_delete (vt, it );
4887}
4888
0d2b891f
RN
4889void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4890{
4891
4892 vtl->current_track = NULL;
0d2b891f
RN
4893 vtl->route_finder_added_track = NULL;
4894 if (vtl->current_tp_track)
4895 trw_layer_cancel_current_tp(vtl, FALSE);
4896
4897 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4898 g_hash_table_remove_all(vtl->routes_iters);
4899 g_hash_table_remove_all(vtl->routes);
4900
4901 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4902
da121f9b 4903 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
4904}
4905
700b0908
QT
4906void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4907{
4908
4909 vtl->current_track = NULL;
ce4bd1cf
RN
4910 vtl->route_finder_added_track = NULL;
4911 if (vtl->current_tp_track)
700b0908 4912 trw_layer_cancel_current_tp(vtl, FALSE);
700b0908
QT
4913
4914 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4915 g_hash_table_remove_all(vtl->tracks_iters);
4916 g_hash_table_remove_all(vtl->tracks);
4917
cd3f311e
RN
4918 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4919
da121f9b 4920 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4921}
4922
4923void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4924{
4925 vtl->current_wp = NULL;
c9570f86 4926 vtl->current_wp_id = NULL;
700b0908
QT
4927 vtl->moving_wp = FALSE;
4928
a8fe53f8
EB
4929 highest_wp_number_reset(vtl);
4930
700b0908
QT
4931 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4932 g_hash_table_remove_all(vtl->waypoints_iters);
4933 g_hash_table_remove_all(vtl->waypoints);
4934
cd3f311e
RN
4935 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4936
da121f9b 4937 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4938}
4939
1c2083ba 4940static void trw_layer_delete_all_tracks ( menu_array_layer values )
c9a5cbf9 4941{
1c2083ba 4942 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4943 // Get confirmation from the user
4944 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4945 _("Are you sure you want to delete all tracks in %s?"),
4946 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4947 vik_trw_layer_delete_all_tracks (vtl);
4948}
4949
1c2083ba 4950static void trw_layer_delete_all_routes ( menu_array_layer values )
c9a5cbf9 4951{
1c2083ba 4952 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4953 // Get confirmation from the user
4954 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4955 _("Are you sure you want to delete all routes in %s?"),
4956 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4957 vik_trw_layer_delete_all_routes (vtl);
c9a5cbf9
RN
4958}
4959
1c2083ba 4960static void trw_layer_delete_all_waypoints ( menu_array_layer values )
0d2b891f 4961{
1c2083ba 4962 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
4963 // Get confirmation from the user
4964 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4965 _("Are you sure you want to delete all waypoints in %s?"),
4966 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4967 vik_trw_layer_delete_all_waypoints (vtl);
4968}
4969
19782ffd 4970static void trw_layer_delete_item ( menu_array_sublayer values )
50a14534 4971{
19782ffd 4972 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 4973 gboolean was_visible = FALSE;
19782ffd 4974 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4975 {
19782ffd 4976 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86 4977 if ( wp && wp->name ) {
19782ffd 4978 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
c9570f86
RN
4979 // Get confirmation from the user
4980 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4981 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4982 _("Are you sure you want to delete the waypoint \"%s\"?"),
c9570f86
RN
4983 wp->name ) )
4984 return;
4985 was_visible = trw_layer_delete_waypoint ( vtl, wp );
785f0336 4986 trw_layer_calculate_bounds_waypoints ( vtl );
c9570f86 4987 }
50a14534 4988 }
19782ffd 4989 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 4990 {
19782ffd 4991 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 4992 if ( trk && trk->name ) {
19782ffd 4993 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
ce4bd1cf
RN
4994 // Get confirmation from the user
4995 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4996 _("Are you sure you want to delete the track \"%s\"?"),
ce4bd1cf
RN
4997 trk->name ) )
4998 return;
4999 was_visible = vik_trw_layer_delete_track ( vtl, trk );
5000 }
50a14534 5001 }
0d2b891f
RN
5002 else
5003 {
19782ffd 5004 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5005 if ( trk && trk->name ) {
19782ffd 5006 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
0d2b891f
RN
5007 // Get confirmation from the user
5008 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 5009 _("Are you sure you want to delete the route \"%s\"?"),
0d2b891f
RN
5010 trk->name ) )
5011 return;
5012 was_visible = vik_trw_layer_delete_route ( vtl, trk );
5013 }
5014 }
50a14534 5015 if ( was_visible )
da121f9b 5016 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
5017}
5018
6761d8a4
RN
5019/**
5020 * Rename waypoint and maintain corresponding name of waypoint in the treeview
5021 */
4f556508 5022void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
6761d8a4
RN
5023{
5024 vik_waypoint_set_name ( wp, new_name );
5025
5026 // Now update the treeview as well
5027 wpu_udata udataU;
5028 udataU.wp = wp;
5029 udataU.uuid = NULL;
5030
5031 // Need key of it for treeview update
5032 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5033
5034 if ( wpf && udataU.uuid ) {
5035 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5036
5037 if ( it ) {
5038 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
c9cac058 5039 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
6761d8a4
RN
5040 }
5041 }
5042}
50a14534 5043
4f556508
RN
5044/**
5045 * Maintain icon of waypoint in the treeview
5046 */
5047void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5048{
5049 // update the treeview
5050 wpu_udata udataU;
5051 udataU.wp = wp;
5052 udataU.uuid = NULL;
5053
5054 // Need key of it for treeview update
5055 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5056
5057 if ( wpf && udataU.uuid ) {
5058 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5059
5060 if ( it ) {
5061 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5062 }
5063 }
5064}
5065
19782ffd 5066static void trw_layer_properties_item ( menu_array_sublayer values )
50a14534 5067{
19782ffd
RN
5068 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5069 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 5070 {
19782ffd 5071 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86
RN
5072
5073 if ( wp && wp->name )
50a14534 5074 {
ac1bde8b 5075 gboolean updated = FALSE;
6761d8a4
RN
5076 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5077 if ( new_name )
5078 trw_layer_waypoint_rename ( vtl, wp, new_name );
50a14534 5079
19782ffd
RN
5080 if ( updated && values[MA_TV_ITER] )
5081 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
70cefedc 5082
ac1bde8b 5083 if ( updated && VIK_LAYER(vtl)->visible )
da121f9b 5084 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
5085 }
5086 }
5087 else
5088 {
0d2b891f 5089 VikTrack *tr;
19782ffd
RN
5090 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5091 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5092 else
19782ffd 5093 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5094
ce4bd1cf 5095 if ( tr && tr->name )
50a14534 5096 {
21700912 5097 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
19782ffd 5098 vtl,
b1453c16 5099 tr,
19782ffd
RN
5100 values[MA_VLP],
5101 values[MA_VVP],
626de648 5102 FALSE );
50a14534
EB
5103 }
5104 }
5105}
5106
626de648
RN
5107/**
5108 * trw_layer_track_statistics:
5109 *
5110 * Show track statistics.
5111 * ATM jump to the stats page in the properties
5112 * TODO: consider separating the stats into an individual dialog?
5113 */
19782ffd 5114static void trw_layer_track_statistics ( menu_array_sublayer values )
626de648 5115{
19782ffd 5116 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
626de648 5117 VikTrack *trk;
19782ffd
RN
5118 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5119 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
626de648 5120 else
19782ffd 5121 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
626de648
RN
5122
5123 if ( trk && trk->name ) {
5124 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5125 vtl,
5126 trk,
19782ffd
RN
5127 values[MA_VLP],
5128 values[MA_VVP],
626de648
RN
5129 TRUE );
5130 }
5131}
5132
b1453c16
RN
5133/*
5134 * Update the treeview of the track id - primarily to update the icon
5135 */
93ee73b3 5136void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
b1453c16
RN
5137{
5138 trku_udata udata;
5139 udata.trk = trk;
5140 udata.uuid = NULL;
5141
5142 gpointer *trkf = NULL;
5143 if ( trk->is_route )
5144 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5145 else
5146 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5147
5148 if ( trkf && udata.uuid ) {
5149
5150 GtkTreeIter *iter = NULL;
5151 if ( trk->is_route )
5152 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5153 else
5154 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5155
5156 if ( iter ) {
5157 // TODO: Make this a function
5158 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5159 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5160 ((trk->color.green & 0xff00) << 8) |
5161 (trk->color.blue & 0xff00);
5162 gdk_pixbuf_fill ( pixbuf, pixel );
5163 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5164 g_object_unref (pixbuf);
5165 }
5166
5167 }
5168}
5169
6bb72350
RN
5170/*
5171 Parameter 1 -> VikLayersPanel
5172 Parameter 2 -> VikLayer
5173 Parameter 3 -> VikViewport
5174*/
5175static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5176{
5177 if ( vlp ) {
be5554c5 5178 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
6bb72350
RN
5179 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5180 }
5181 else {
5182 /* since vlp not set, vl & vvp should be valid instead! */
5183 if ( vl && vvp ) {
be5554c5 5184 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
da121f9b 5185 vik_layer_emit_update ( VIK_LAYER(vl) );
6bb72350
RN
5186 }
5187 }
50a14534
EB
5188}
5189
19782ffd 5190static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
50a14534 5191{
19782ffd 5192 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5193 VikTrack *track;
19782ffd
RN
5194 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5195 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5196 else
19782ffd 5197 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5198
5199 if ( track && track->trackpoints )
215ebe59 5200 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
50a14534
EB
5201}
5202
19782ffd 5203static void trw_layer_goto_track_center ( menu_array_sublayer values )
50a14534 5204{
19782ffd 5205 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5206 VikTrack *track;
19782ffd
RN
5207 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5208 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5209 else
19782ffd 5210 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5211
867f4673 5212 if ( track && track->trackpoints )
50a14534
EB
5213 {
5214 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5215 VikCoord coord;
867f4673 5216 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
50a14534
EB
5217 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5218 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
0d2b891f 5219 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
19782ffd 5220 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
50a14534
EB
5221 }
5222}
5223
19782ffd 5224static void trw_layer_convert_track_route ( menu_array_sublayer values )
2f5d7ea1 5225{
19782ffd 5226 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
2f5d7ea1 5227 VikTrack *trk;
19782ffd
RN
5228 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5229 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
2f5d7ea1 5230 else
19782ffd 5231 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
2f5d7ea1
RN
5232
5233 if ( !trk )
5234 return;
5235
5236 // Converting a track to a route can be a bit more complicated,
5237 // so give a chance to change our minds:
5238 if ( !trk->is_route &&
5239 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5240 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5241
5242 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5243 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5244 return;
5245}
5246
5247 // Copy it
03817fbf 5248 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
2f5d7ea1
RN
5249
5250 // Convert
5251 trk_copy->is_route = !trk_copy->is_route;
5252
5253 // ATM can't set name to self - so must create temporary copy
5254 gchar *name = g_strdup ( trk_copy->name );
5255
5256 // Delete old one and then add new one
5257 if ( trk->is_route ) {
5258 vik_trw_layer_delete_route ( vtl, trk );
5259 vik_trw_layer_add_track ( vtl, name, trk_copy );
5260 }
5261 else {
5262 // Extra route conversion bits...
5263 vik_track_merge_segments ( trk_copy );
5264 vik_track_to_routepoints ( trk_copy );
5265
5266 vik_trw_layer_delete_track ( vtl, trk );
5267 vik_trw_layer_add_route ( vtl, name, trk_copy );
5268 }
5269 g_free ( name );
5270
5271 // Update in case color of track / route changes when moving between sublayers
19782ffd 5272 vik_layer_emit_update ( VIK_LAYER(vtl) );
2f5d7ea1
RN
5273}
5274
19782ffd 5275static void trw_layer_anonymize_times ( menu_array_sublayer values )
76b14439 5276{
19782ffd 5277 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
76b14439 5278 VikTrack *track;
19782ffd
RN
5279 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5280 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
76b14439 5281 else
19782ffd 5282 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
76b14439
RN
5283
5284 if ( track )
5285 vik_track_anonymize_times ( track );
5286}
2f5d7ea1 5287
19782ffd 5288static void trw_layer_extend_track_end ( menu_array_sublayer values )
8fb71d6c 5289{
19782ffd 5290 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5291 VikTrack *track;
19782ffd
RN
5292 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5293 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5294 else
19782ffd 5295 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5296
5297 if ( !track )
5298 return;
8fb71d6c
EB
5299
5300 vtl->current_track = track;
e37b2a6d 5301 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
5302
5303 if ( track->trackpoints )
215ebe59 5304 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
8fb71d6c
EB
5305}
5306
e3154bef 5307/**
7ff7d728 5308 * extend a track using route finder
e3154bef 5309 */
19782ffd 5310static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
a7955c1d 5311{
19782ffd
RN
5312 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5313 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
008e972c
RN
5314 if ( !track )
5315 return;
a7955c1d 5316
37baade6 5317 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
ee43b101 5318 vtl->current_track = track;
7ff7d728 5319 vtl->route_finder_started = TRUE;
a7955c1d 5320
ee43b101
MH
5321 if ( track->trackpoints )
5322 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
a7955c1d 5323}
a7955c1d 5324
4d333042 5325/**
4d333042 5326 *
4d333042 5327 */
667fda15 5328static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4d333042 5329{
73747c42
RN
5330 // If have a vlp then perform a basic test to see if any DEM info available...
5331 if ( vlp ) {
5332 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5333
5334 if ( !g_list_length(dems) ) {
5335 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
667fda15 5336 return FALSE;
73747c42
RN
5337 }
5338 }
667fda15
RN
5339 return TRUE;
5340}
5341
5342/**
5343 * apply_dem_data_common:
5344 *
5345 * A common function for applying the DEM values and reporting the results.
5346 */
5347static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5348{
5349 if ( !trw_layer_dem_test ( vtl, vlp ) )
5350 return;
73747c42 5351
4d333042
RN
5352 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5353 // Inform user how much was changed
5354 gchar str[64];
5355 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5356 g_snprintf(str, 64, tmp_str, changed);
5357 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5358}
5359
19782ffd 5360static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
4d333042 5361{
19782ffd 5362 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4d333042 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] );
4d333042 5366 else
19782ffd 5367 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4d333042
RN
5368
5369 if ( track )
19782ffd 5370 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
4d333042
RN
5371}
5372
19782ffd 5373static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
ad0a8c2d 5374{
19782ffd 5375 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5376 VikTrack *track;
19782ffd
RN
5377 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5378 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5379 else
19782ffd 5380 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ad0a8c2d 5381
0d2b891f 5382 if ( track )
19782ffd 5383 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
ad0a8c2d
EB
5384}
5385
81ac2835
RN
5386/**
5387 * smooth_it:
5388 *
5389 * A common function for applying the elevation smoothing and reporting the results.
5390 */
5391static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5392{
5393 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5394 // Inform user how much was changed
5395 gchar str[64];
5396 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5397 g_snprintf(str, 64, tmp_str, changed);
5398 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5399}
5400
5401/**
5402 *
5403 */
19782ffd 5404static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
81ac2835 5405{
19782ffd 5406 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5407 VikTrack *track;
19782ffd
RN
5408 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5409 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5410 else
19782ffd 5411 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5412
5413 if ( !track )
5414 return;
5415
5416 smooth_it ( vtl, track, FALSE );
5417}
5418
19782ffd 5419static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
81ac2835 5420{
19782ffd 5421 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5422 VikTrack *track;
19782ffd
RN
5423 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5424 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5425 else
19782ffd 5426 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5427
5428 if ( !track )
5429 return;
5430
5431 smooth_it ( vtl, track, TRUE );
5432}
5433
667fda15
RN
5434/**
5435 * Commonal helper function
5436 */
5437static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5438{
5439 gchar str[64];
5440 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5441 g_snprintf(str, 64, tmp_str, changed);
5442 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5443}
5444
19782ffd 5445static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
667fda15 5446{
19782ffd
RN
5447 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5448 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5449
5450 if ( !trw_layer_dem_test ( vtl, vlp ) )
5451 return;
5452
5453 gint changed = 0;
19782ffd 5454 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5455 // Single Waypoint
19782ffd 5456 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5457 if ( wp )
5458 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5459 }
5460 else {
5461 // All waypoints
5462 GHashTableIter iter;
5463 gpointer key, value;
5464
5465 g_hash_table_iter_init ( &iter, vtl->waypoints );
5466 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5467 VikWaypoint *wp = VIK_WAYPOINT(value);
5468 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5469 }
5470 }
5471 wp_changed_message ( vtl, changed );
5472}
5473
19782ffd 5474static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
667fda15 5475{
19782ffd
RN
5476 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5477 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5478
5479 if ( !trw_layer_dem_test ( vtl, vlp ) )
5480 return;
5481
5482 gint changed = 0;
19782ffd 5483 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5484 // Single Waypoint
19782ffd 5485 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5486 if ( wp )
5487 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5488 }
5489 else {
5490 // All waypoints
5491 GHashTableIter iter;
5492 gpointer key, value;
5493
5494 g_hash_table_iter_init ( &iter, vtl->waypoints );
5495 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5496 VikWaypoint *wp = VIK_WAYPOINT(value);
5497 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5498 }
5499 }
5500 wp_changed_message ( vtl, changed );
5501}
5502
19782ffd 5503static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
50a14534 5504{
19782ffd 5505 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5506 VikTrack *track;
19782ffd
RN
5507 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5508 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5509 else
19782ffd 5510 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5511
5512 if ( !track )
5513 return;
215ebe59 5514 if ( !track->trackpoints )
50a14534 5515 return;
215ebe59 5516 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
50a14534
EB
5517}
5518
19782ffd 5519static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
03e7da75 5520{
19782ffd 5521 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5522 VikTrack *track;
19782ffd
RN
5523 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5524 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5525 else
19782ffd 5526 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5527
5528 if ( !track )
5529 return;
5530
5531 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
03e7da75
RN
5532 if ( !vtp )
5533 return;
19782ffd 5534 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
03e7da75
RN
5535}
5536
19782ffd 5537static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
c28faca8 5538{
19782ffd 5539 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5540 VikTrack *track;
19782ffd
RN
5541 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5542 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5543 else
19782ffd 5544 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5545
5546 if ( !track )
5547 return;
5548
5549 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
c28faca8
RN
5550 if ( !vtp )
5551 return;
19782ffd 5552 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8
RN
5553}
5554
19782ffd 5555static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
c28faca8 5556{
19782ffd 5557 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5558 VikTrack *track;
19782ffd
RN
5559 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5560 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5561 else
19782ffd 5562 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5563
5564 if ( !track )
5565 return;
5566
5567 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
c28faca8
RN
5568 if ( !vtp )
5569 return;
19782ffd 5570 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8 5571}
111fa174 5572
116ea336 5573/*
469113fb 5574 * Automatically change the viewport to center on the track and zoom to see the extent of the track
116ea336 5575 */
19782ffd 5576static void trw_layer_auto_track_view ( menu_array_sublayer values )
469113fb 5577{
19782ffd 5578 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5579 VikTrack *trk;
19782ffd
RN
5580 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5581 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5582 else
19782ffd 5583 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5584
867f4673 5585 if ( trk && trk->trackpoints )
469113fb
RN
5586 {
5587 struct LatLon maxmin[2] = { {0,0}, {0,0} };
867f4673 5588 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
19782ffd
RN
5589 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5590 if ( values[MA_VLP] )
5591 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
6bb72350 5592 else
11651e51 5593 vik_layer_emit_update ( VIK_LAYER(vtl) );
469113fb
RN
5594 }
5595}
5596
1a3be6a8
GB
5597/*
5598 * Refine the selected track/route with a routing engine.
5599 * The routing engine is selected by the user, when requestiong the job.
5600 */
19782ffd 5601static void trw_layer_route_refine ( menu_array_sublayer values )
1a3be6a8
GB
5602{
5603 static gint last_engine = 0;
19782ffd 5604 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1a3be6a8
GB
5605 VikTrack *trk;
5606
19782ffd
RN
5607 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5608 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
1a3be6a8 5609 else
19782ffd 5610 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
1a3be6a8
GB
5611
5612 if ( trk && trk->trackpoints )
5613 {
80846d50
GB
5614 /* Check size of the route */
5615 int nb = vik_track_get_tp_count(trk);
5616 if (nb > 100) {
5617 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5618 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5619 GTK_MESSAGE_WARNING,
5620 GTK_BUTTONS_OK_CANCEL,
5621 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5622 nb);
5623 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5624 gtk_widget_destroy ( dialog );
5625 if (response != GTK_RESPONSE_OK )
5626 return;
5627 }
1a3be6a8
GB
5628 /* Select engine from dialog */
5629 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5630 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5631 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5632 GTK_STOCK_CANCEL,
5633 GTK_RESPONSE_REJECT,
5634 GTK_STOCK_OK,
5635 GTK_RESPONSE_ACCEPT,
5636 NULL);
5637 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5638 gtk_widget_show_all(label);
5639
5640 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5641
5642 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5643 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5644 gtk_widget_show_all(combo);
5645
5646 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5647
5648 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5649
5650 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5651 {
5652 /* Dialog validated: retrieve selected engine and do the job */
5653 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5654 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5655
5656 /* Change cursor */
19782ffd 5657 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5658
5659 /* Force saving track */
5660 /* FIXME: remove or rename this hack */
5661 vtl->route_finder_check_added_track = TRUE;
5662
5663 /* the job */
5664 vik_routing_engine_refine (routing, vtl, trk);
5665
5666 /* FIXME: remove or rename this hack */
5667 if ( vtl->route_finder_added_track )
5668 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5669
5670 vtl->route_finder_added_track = NULL;
5671 vtl->route_finder_check_added_track = FALSE;
5672
5673 vik_layer_emit_update ( VIK_LAYER(vtl) );
5674
5675 /* Restore cursor */
19782ffd 5676 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5677 }
5678 gtk_widget_destroy ( dialog );
5679 }
5680}
5681
19782ffd 5682static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
c95d6b00 5683{
19782ffd 5684 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c95d6b00
RN
5685 trw_layer_tpwin_init ( vtl );
5686}
5687
111fa174
AF
5688/*************************************
5689 * merge/split by time routines
5690 *************************************/
5691
15f45edc 5692/* called for each key in track hash table.
5780603d 5693 * If the current track has the same time stamp type, add it to the result,
15f45edc
QT
5694 * except the one pointed by "exclude".
5695 * set exclude to NULL if there is no exclude to check.
5780603d 5696 * Note that the result is in reverse (for performance reasons).
15f45edc
QT
5697 */
5698typedef struct {
5699 GList **result;
9cdc601f 5700 VikTrack *exclude;
5780603d 5701 gboolean with_timestamps;
15f45edc 5702} twt_udata;
5780603d 5703static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
15f45edc
QT
5704{
5705 twt_udata *user_data = udata;
5706 VikTrackpoint *p1, *p2;
215ebe59 5707 VikTrack *trk = VIK_TRACK(value);
9cdc601f 5708 if (trk == user_data->exclude) {
15f45edc
QT
5709 return;
5710 }
5711
215ebe59
RN
5712 if (trk->trackpoints) {
5713 p1 = vik_track_get_tp_first(trk);
5714 p2 = vik_track_get_tp_last(trk);
15f45edc 5715
5780603d
RN
5716 if ( user_data->with_timestamps ) {
5717 if (!p1->has_timestamp || !p2->has_timestamp) {
5718 return;
5719 }
5720 }
5721 else {
5722 // Don't add tracks with timestamps when getting non timestamp tracks
5723 if (p1->has_timestamp || p2->has_timestamp) {
5724 return;
5725 }
15f45edc 5726 }
15f45edc
QT
5727 }
5728
5729 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5730}
5731
6252ee1d
RN
5732/**
5733 * find_nearby_tracks_by_time:
5734 *
5735 * Called for each track in track hash table.
5736 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5737 * to the current track, then the current track is added to the list in user_data[0]
111fa174 5738 */
fc92c977 5739static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
111fa174 5740{
fc92c977 5741 VikTrack *trk = VIK_TRACK(value);
111fa174
AF
5742
5743 GList **nearby_tracks = ((gpointer *)user_data)[0];
6252ee1d
RN
5744 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5745
5746 if ( !orig_trk || !orig_trk->trackpoints )
5747 return;
111fa174 5748
a61b2619
AF
5749 /* outline:
5750 * detect reasons for not merging, and return
5751 * if no reason is found not to merge, then do it.
5752 */
111fa174 5753
9cdc601f 5754 twt_udata *udata = user_data;
fc92c977 5755 // Exclude the original track from the compiled list
9cdc601f 5756 if (trk == udata->exclude) {
111fa174
AF
5757 return;
5758 }
5759
6252ee1d
RN
5760 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5761 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
111fa174 5762
fc92c977 5763 if (trk->trackpoints) {
6252ee1d
RN
5764
5765 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5766 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
a61b2619
AF
5767
5768 if (!p1->has_timestamp || !p2->has_timestamp) {
fc92c977 5769 //g_print("no timestamp\n");
a61b2619
AF
5770 return;
5771 }
111fa174 5772
fc92c977
RN
5773 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5774 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5775 if (! (abs(t1 - p2->timestamp) < threshold ||
a61b2619 5776 /* p1 p2 t1 t2 */
fc92c977 5777 abs(p1->timestamp - t2) < threshold)
a61b2619
AF
5778 /* t1 t2 p1 p2 */
5779 ) {
5780 return;
5781 }
111fa174 5782 }
a61b2619 5783
fc92c977 5784 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
111fa174
AF
5785}
5786
5787/* comparison function used to sort tracks; a and b are hash table keys */
02b5d347 5788/* Not actively used - can be restored if needed
111fa174
AF
5789static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5790{
5791 GHashTable *tracks = user_data;
5792 time_t t1, t2;
5793
5794 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5795 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5796
5797 if (t1 < t2) return -1;
5798 if (t1 > t2) return 1;
5799 return 0;
5800}
02b5d347 5801*/
111fa174
AF
5802
5803/* comparison function used to sort trackpoints */
5804static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5805{
5806 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5807
5808 if (t1 < t2) return -1;
5809 if (t1 > t2) return 1;
5810 return 0;
5811}
5812
fb2306f7
RN
5813/**
5814 * comparison function which can be used to sort tracks or waypoints by name
5815 */
5816static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5817{
5818 const gchar* namea = (const gchar*) a;
5819 const gchar* nameb = (const gchar*) b;
5820 if ( namea == NULL || nameb == NULL)
5821 return 0;
5822 else
5823 // Same sort method as used in the vik_treeview_*_alphabetize functions
5824 return strcmp ( namea, nameb );
5825}
fb2306f7 5826
5780603d
RN
5827/**
5828 * Attempt to merge selected track with other tracks specified by the user
5829 * Tracks to merge with must be of the same 'type' as the selected track -
5830 * either all with timestamps, or all without timestamps
5831 */
19782ffd 5832static void trw_layer_merge_with_other ( menu_array_sublayer values )
291edcab 5833{
19782ffd 5834 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5780603d 5835 GList *other_tracks = NULL;
0d2b891f 5836 GHashTable *ght_tracks;
19782ffd 5837 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5838 ght_tracks = vtl->routes;
5839 else
5840 ght_tracks = vtl->tracks;
5841
19782ffd 5842 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5843
5844 if ( !track )
5845 return;
15f45edc 5846
5780603d 5847 if ( !track->trackpoints )
15f45edc 5848 return;
15f45edc 5849
c1564279 5850 twt_udata udata;
5780603d 5851 udata.result = &other_tracks;
9cdc601f 5852 udata.exclude = track;
5780603d
RN
5853 // Allow merging with 'similar' time type time tracks
5854 // i.e. either those times, or those without
215ebe59 5855 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
291edcab 5856
0d2b891f 5857 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5780603d
RN
5858 other_tracks = g_list_reverse(other_tracks);
5859
5860 if ( !other_tracks ) {
5861 if ( udata.with_timestamps )
5862 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5863 else
5864 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
15f45edc
QT
5865 return;
5866 }
5867
8352326e 5868 // Sort alphabetically for user presentation
ce4bd1cf
RN
5869 // Convert into list of names for usage with dialog function
5870 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5871 GList *other_tracks_names = NULL;
5872 GList *iter = g_list_first ( other_tracks );
5873 while ( iter ) {
0d2b891f 5874 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
ce4bd1cf
RN
5875 iter = g_list_next ( iter );
5876 }
5877
5878 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
8352326e 5879
7767aa02 5880 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5881 other_tracks_names,
5882 TRUE,
5883 _("Merge with..."),
5884 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5780603d 5885 g_list_free(other_tracks);
ce4bd1cf 5886 g_list_free(other_tracks_names);
291edcab 5887
7767aa02 5888 if (merge_list)
291edcab 5889 {
7767aa02
QT
5890 GList *l;
5891 for (l = merge_list; l != NULL; l = g_list_next(l)) {
0d2b891f
RN
5892 VikTrack *merge_track;
5893 if ( track->is_route )
5894 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5895 else
5896 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5897
7767aa02 5898 if (merge_track) {
0a9ed7dc 5899 vik_track_steal_and_append_trackpoints ( track, merge_track );
0d2b891f
RN
5900 if ( track->is_route )
5901 vik_trw_layer_delete_route (vtl, merge_track);
5902 else
5903 vik_trw_layer_delete_track (vtl, merge_track);
7767aa02
QT
5904 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5905 }
291edcab 5906 }
7767aa02
QT
5907 for (l = merge_list; l != NULL; l = g_list_next(l))
5908 g_free(l->data);
5909 g_list_free(merge_list);
20981fd6 5910
da121f9b 5911 vik_layer_emit_update( VIK_LAYER(vtl) );
291edcab 5912 }
291edcab
HR
5913}
5914
9c34f614
RN
5915// c.f. trw_layer_sorted_track_id_by_name_list
5916// but don't add the specified track to the list (normally current track)
5917static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5918{
5919 twt_udata *user_data = udata;
5920
5921 // Skip self
9cdc601f 5922 if (trk == user_data->exclude) {
9c34f614
RN
5923 return;
5924 }
5925
5926 // Sort named list alphabetically
5927 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5928}
5929
5930/**
0d2b891f 5931 * Join - this allows combining 'tracks' and 'track routes'
9c34f614
RN
5932 * i.e. doesn't care about whether tracks have consistent timestamps
5933 * ATM can only append one track at a time to the currently selected track
5934 */
19782ffd 5935static void trw_layer_append_track ( menu_array_sublayer values )
9c34f614
RN
5936{
5937
19782ffd 5938 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f
RN
5939 VikTrack *trk;
5940 GHashTable *ght_tracks;
19782ffd 5941 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5942 ght_tracks = vtl->routes;
5943 else
5944 ght_tracks = vtl->tracks;
5945
19782ffd 5946 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5947
5948 if ( !trk )
5949 return;
9c34f614
RN
5950
5951 GList *other_tracks_names = NULL;
5952
5953 // Sort alphabetically for user presentation
5954 // Convert into list of names for usage with dialog function
5955 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5956 twt_udata udata;
5957 udata.result = &other_tracks_names;
9cdc601f 5958 udata.exclude = trk;
9c34f614 5959
0d2b891f 5960 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
9c34f614
RN
5961
5962 // Note the limit to selecting one track only
5963 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5964 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5965 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5966 other_tracks_names,
5967 FALSE,
5968 trk->is_route ? _("Append Route"): _("Append Track"),
5969 trk->is_route ? _("Select the route to append after the current route") :
5970 _("Select the track to append after the current track") );
9c34f614
RN
5971
5972 g_list_free(other_tracks_names);
5973
5974 // It's a list, but shouldn't contain more than one other track!
5975 if ( append_list ) {
5976 GList *l;
5977 for (l = append_list; l != NULL; l = g_list_next(l)) {
5978 // TODO: at present this uses the first track found by name,
5979 // which with potential multiple same named tracks may not be the one selected...
0d2b891f
RN
5980 VikTrack *append_track;
5981 if ( trk->is_route )
5982 append_track = vik_trw_layer_get_route ( vtl, l->data );
5983 else
5984 append_track = vik_trw_layer_get_track ( vtl, l->data );
5985
9c34f614 5986 if ( append_track ) {
0a9ed7dc 5987 vik_track_steal_and_append_trackpoints ( trk, append_track );
0d2b891f
RN
5988 if ( trk->is_route )
5989 vik_trw_layer_delete_route (vtl, append_track);
5990 else
5991 vik_trw_layer_delete_track (vtl, append_track);
9c34f614
RN
5992 }
5993 }
5994 for (l = append_list; l != NULL; l = g_list_next(l))
5995 g_free(l->data);
5996 g_list_free(append_list);
20981fd6 5997
da121f9b 5998 vik_layer_emit_update( VIK_LAYER(vtl) );
9c34f614
RN
5999 }
6000}
6001
6b5b6c47
RN
6002/**
6003 * Very similar to trw_layer_append_track for joining
6004 * but this allows selection from the 'other' list
6005 * If a track is selected, then is shows routes and joins the selected one
6006 * If a route is selected, then is shows tracks and joins the selected one
6007 */
19782ffd 6008static void trw_layer_append_other ( menu_array_sublayer values )
6b5b6c47
RN
6009{
6010
19782ffd 6011 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6b5b6c47
RN
6012 VikTrack *trk;
6013 GHashTable *ght_mykind, *ght_others;
19782ffd 6014 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6b5b6c47
RN
6015 ght_mykind = vtl->routes;
6016 ght_others = vtl->tracks;
6017 }
6018 else {
6019 ght_mykind = vtl->tracks;
6020 ght_others = vtl->routes;
6021 }
6022
19782ffd 6023 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6b5b6c47
RN
6024
6025 if ( !trk )
6026 return;
6027
6028 GList *other_tracks_names = NULL;
6029
6030 // Sort alphabetically for user presentation
6031 // Convert into list of names for usage with dialog function
6032 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6033 twt_udata udata;
6034 udata.result = &other_tracks_names;
9cdc601f 6035 udata.exclude = trk;
6b5b6c47
RN
6036
6037 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6038
6039 // Note the limit to selecting one track only
6040 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6041 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6042 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6043 other_tracks_names,
6044 FALSE,
6045 trk->is_route ? _("Append Track"): _("Append Route"),
6046 trk->is_route ? _("Select the track to append after the current route") :
6047 _("Select the route to append after the current track") );
6048
6049 g_list_free(other_tracks_names);
6050
6051 // It's a list, but shouldn't contain more than one other track!
6052 if ( append_list ) {
6053 GList *l;
6054 for (l = append_list; l != NULL; l = g_list_next(l)) {
6055 // TODO: at present this uses the first track found by name,
6056 // which with potential multiple same named tracks may not be the one selected...
6057
6058 // Get FROM THE OTHER TYPE list
6059 VikTrack *append_track;
6060 if ( trk->is_route )
6061 append_track = vik_trw_layer_get_track ( vtl, l->data );
6062 else
6063 append_track = vik_trw_layer_get_route ( vtl, l->data );
6064
6065 if ( append_track ) {
6066
6067 if ( !append_track->is_route &&
6068 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6069 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6070
6071 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6072 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6073 vik_track_merge_segments ( append_track );
6074 vik_track_to_routepoints ( append_track );
6075 }
6076 else {
6077 break;
6078 }
6079 }
6080
0a9ed7dc 6081 vik_track_steal_and_append_trackpoints ( trk, append_track );
6b5b6c47
RN
6082
6083 // Delete copied which is FROM THE OTHER TYPE list
6084 if ( trk->is_route )
6085 vik_trw_layer_delete_track (vtl, append_track);
6086 else
6087 vik_trw_layer_delete_route (vtl, append_track);
6088 }
6089 }
6090 for (l = append_list; l != NULL; l = g_list_next(l))
6091 g_free(l->data);
6092 g_list_free(append_list);
da121f9b 6093 vik_layer_emit_update( VIK_LAYER(vtl) );
6b5b6c47
RN
6094 }
6095}
6096
24774c43 6097/* merge by segments */
19782ffd 6098static void trw_layer_merge_by_segment ( menu_array_sublayer values )
24774c43 6099{
19782ffd
RN
6100 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6101 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
24774c43
RN
6102 guint segments = vik_track_merge_segments ( trk );
6103 // NB currently no need to redraw as segments not actually shown on the display
6104 // However inform the user of what happened:
6105 gchar str[64];
6106 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6107 g_snprintf(str, 64, tmp_str, segments);
6108 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6109}
6110
111fa174 6111/* merge by time routine */
19782ffd 6112static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
111fa174 6113{
19782ffd 6114 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a61b2619 6115
02b5d347 6116 //time_t t1, t2;
111fa174 6117
c1564279 6118 GList *tracks_with_timestamp = NULL;
19782ffd 6119 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 6120 if (orig_trk->trackpoints &&
215ebe59 6121 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
c1564279 6122 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
c1564279
RN
6123 return;
6124 }
6125
6126 twt_udata udata;
6127 udata.result = &tracks_with_timestamp;
9cdc601f 6128 udata.exclude = orig_trk;
5780603d
RN
6129 udata.with_timestamps = TRUE;
6130 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
c1564279
RN
6131 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6132
6133 if (!tracks_with_timestamp) {
6134 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
c1564279
RN
6135 return;
6136 }
6137 g_list_free(tracks_with_timestamp);
6138
fc92c977
RN
6139 static guint threshold_in_minutes = 1;
6140 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6141 _("Merge Threshold..."),
6142 _("Merge when time between tracks less than:"),
6143 &threshold_in_minutes)) {
111fa174
AF
6144 return;
6145 }
6146
fc92c977
RN
6147 // keep attempting to merge all tracks until no merges within the time specified is possible
6148 gboolean attempt_merge = TRUE;
6149 GList *nearby_tracks = NULL;
6150 GList *trps;
6151 static gpointer params[3];
6152
6153 while ( attempt_merge ) {
6154
6155 // Don't try again unless tracks have changed
6156 attempt_merge = FALSE;
111fa174 6157
ce4bd1cf 6158 trps = orig_trk->trackpoints;
111fa174
AF
6159 if ( !trps )
6160 return;
6161
111fa174
AF
6162 if (nearby_tracks) {
6163 g_list_free(nearby_tracks);
6164 nearby_tracks = NULL;
6165 }
6166
111fa174 6167 params[0] = &nearby_tracks;
6252ee1d 6168 params[1] = orig_trk;
fc92c977 6169 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
111fa174
AF
6170
6171 /* get a list of adjacent-in-time tracks */
fc92c977 6172 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
111fa174 6173
111fa174 6174 /* merge them */
fc92c977
RN
6175 GList *l = nearby_tracks;
6176 while ( l ) {
fc92c977 6177 /* remove trackpoints from merged track, delete track */
0a9ed7dc 6178 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
fc92c977 6179 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
111fa174 6180
fc92c977
RN
6181 // Tracks have changed, therefore retry again against all the remaining tracks
6182 attempt_merge = TRUE;
ce4bd1cf 6183
fc92c977 6184 l = g_list_next(l);
111fa174 6185 }
fc92c977
RN
6186
6187 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6188 }
6189
111fa174 6190 g_list_free(nearby_tracks);
20981fd6 6191
da121f9b 6192 vik_layer_emit_update( VIK_LAYER(vtl) );
111fa174
AF
6193}
6194
84d3d9f9 6195/**
9914238e 6196 * Split a track at the currently selected trackpoint
84d3d9f9 6197 */
0d2b891f 6198static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
84d3d9f9
RN
6199{
6200 if ( !vtl->current_tpl )
6201 return;
6202
6203 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
0d2b891f 6204 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
9914238e 6205 if ( name ) {
03817fbf 6206 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
84d3d9f9
RN
6207 GList *newglist = g_list_alloc ();
6208 newglist->prev = NULL;
6209 newglist->next = vtl->current_tpl->next;
6210 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6211 tr->trackpoints = newglist;
6212
6213 vtl->current_tpl->next->prev = newglist; /* end old track here */
6214 vtl->current_tpl->next = NULL;
6215
20981fd6
RN
6216 // Bounds of the selected track changed due to the split
6217 vik_track_calculate_bounds ( vtl->current_tp_track );
6218
84d3d9f9
RN
6219 vtl->current_tpl = newglist; /* change tp to first of new track. */
6220 vtl->current_tp_track = tr;
6221
0d2b891f
RN
6222 if ( tr->is_route )
6223 vik_trw_layer_add_route ( vtl, name, tr );
6224 else
6225 vik_trw_layer_add_track ( vtl, name, tr );
84d3d9f9 6226
20981fd6
RN
6227 // Bounds of the new track created by the split
6228 vik_track_calculate_bounds ( tr );
6229
84d3d9f9
RN
6230 trku_udata udata;
6231 udata.trk = tr;
6232 udata.uuid = NULL;
6233
6234 // Also need id of newly created track
0d2b891f
RN
6235 gpointer *trkf;
6236 if ( tr->is_route )
6237 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6238 else
6239 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6240
84d3d9f9
RN
6241 if ( trkf && udata.uuid )
6242 vtl->current_tp_id = udata.uuid;
6243 else
6244 vtl->current_tp_id = NULL;
6245
da121f9b 6246 vik_layer_emit_update(VIK_LAYER(vtl));
84d3d9f9 6247 }
1613e468 6248 g_free ( name );
84d3d9f9
RN
6249 }
6250}
6251
111fa174 6252/* split by time routine */
19782ffd 6253static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
111fa174 6254{
19782ffd
RN
6255 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6256 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
111fa174
AF
6257 GList *trps = track->trackpoints;
6258 GList *iter;
6259 GList *newlists = NULL;
6260 GList *newtps = NULL;
111fa174
AF
6261 static guint thr = 1;
6262
6263 time_t ts, prev_ts;
6264
6265 if ( !trps )
6266 return;
6267
19782ffd 6268 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4c77d5e0
GB
6269 _("Split Threshold..."),
6270 _("Split when time between trackpoints exceeds:"),
111fa174
AF
6271 &thr)) {
6272 return;
6273 }
6274
6275 /* iterate through trackpoints, and copy them into new lists without touching original list */
6276 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6277 iter = trps;
6278
6279 while (iter) {
6280 ts = VIK_TRACKPOINT(iter->data)->timestamp;
1e3b44c4
RN
6281
6282 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
111fa174 6283 if (ts < prev_ts) {
1e3b44c4
RN
6284 gchar tmp_str[64];
6285 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6286 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6287 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6288 tmp_str ) ) {
19782ffd 6289 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
1e3b44c4 6290 }
111fa174
AF
6291 return;
6292 }
1e3b44c4 6293
111fa174
AF
6294 if (ts - prev_ts > thr*60) {
6295 /* flush accumulated trackpoints into new list */
aa9887a1 6296 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6297 newtps = NULL;
6298 }
6299
6300 /* accumulate trackpoint copies in newtps, in reverse order */
6301 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6302 prev_ts = ts;
6303 iter = g_list_next(iter);
6304 }
6305 if (newtps) {
aa9887a1 6306 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6307 }
6308
6309 /* put lists of trackpoints into tracks */
6310 iter = newlists;
c9d8f273
RN
6311 // Only bother updating if the split results in new tracks
6312 if (g_list_length (newlists) > 1) {
6313 while (iter) {
6314 gchar *new_tr_name;
6315 VikTrack *tr;
111fa174 6316
03817fbf 6317 tr = vik_track_copy ( track, FALSE );
c9d8f273 6318 tr->trackpoints = (GList *)(iter->data);
111fa174 6319
784b6042
RN
6320 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6321 vik_trw_layer_add_track(vtl, new_tr_name, tr);
1613e468 6322 g_free ( new_tr_name );
20981fd6 6323 vik_track_calculate_bounds ( tr );
c9d8f273
RN
6324 iter = g_list_next(iter);
6325 }
ce4bd1cf 6326 // Remove original track and then update the display
784b6042 6327 vik_trw_layer_delete_track (vtl, track);
19782ffd 6328 vik_layer_emit_update(VIK_LAYER(vtl));
111fa174
AF
6329 }
6330 g_list_free(newlists);
111fa174
AF
6331}
6332
af2341f3
RN
6333/**
6334 * Split a track by the number of points as specified by the user
6335 */
19782ffd 6336static void trw_layer_split_by_n_points ( menu_array_sublayer values )
af2341f3 6337{
19782ffd 6338 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6339 VikTrack *track;
19782ffd
RN
6340 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6341 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6342 else
19782ffd 6343 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6344
6345 if ( !track )
6346 return;
af2341f3
RN
6347
6348 // Check valid track
6349 GList *trps = track->trackpoints;
6350 if ( !trps )
6351 return;
6352
19782ffd 6353 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
af2341f3
RN
6354 _("Split Every Nth Point"),
6355 _("Split on every Nth point:"),
6356 250, // Default value as per typical limited track capacity of various GPS devices
6357 2, // Min
6358 65536, // Max
6359 5); // Step
6360 // Was a valid number returned?
6361 if (!points)
6362 return;
6363
6364 // Now split...
6365 GList *iter;
6366 GList *newlists = NULL;
6367 GList *newtps = NULL;
6368 gint count = 0;
6369 iter = trps;
6370
6371 while (iter) {
6372 /* accumulate trackpoint copies in newtps, in reverse order */
6373 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6374 count++;
6375 if (count >= points) {
6376 /* flush accumulated trackpoints into new list */
6377 newlists = g_list_append(newlists, g_list_reverse(newtps));
6378 newtps = NULL;
6379 count = 0;
6380 }
6381 iter = g_list_next(iter);
6382 }
6383
6384 // If there is a remaining chunk put that into the new split list
6385 // This may well be the whole track if no split points were encountered
6386 if (newtps) {
6387 newlists = g_list_append(newlists, g_list_reverse(newtps));
6388 }
6389
6390 /* put lists of trackpoints into tracks */
6391 iter = newlists;
af2341f3
RN
6392 // Only bother updating if the split results in new tracks
6393 if (g_list_length (newlists) > 1) {
6394 while (iter) {
6395 gchar *new_tr_name;
6396 VikTrack *tr;
6397
03817fbf 6398 tr = vik_track_copy ( track, FALSE );
af2341f3
RN
6399 tr->trackpoints = (GList *)(iter->data);
6400
0d2b891f
RN
6401 if ( track->is_route ) {
6402 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6403 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6404 }
6405 else {
6406 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6407 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6408 }
1613e468 6409 g_free ( new_tr_name );
20981fd6
RN
6410 vik_track_calculate_bounds ( tr );
6411
af2341f3
RN
6412 iter = g_list_next(iter);
6413 }
6414 // Remove original track and then update the display
0d2b891f
RN
6415 if ( track->is_route )
6416 vik_trw_layer_delete_route (vtl, track);
6417 else
6418 vik_trw_layer_delete_track (vtl, track);
19782ffd 6419 vik_layer_emit_update(VIK_LAYER(vtl));
af2341f3
RN
6420 }
6421 g_list_free(newlists);
6422}
6423
a4aefe69
RN
6424/**
6425 * Split a track at the currently selected trackpoint
6426 */
19782ffd 6427static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
a4aefe69 6428{
19782ffd
RN
6429 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6430 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
0d2b891f 6431 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
a4aefe69 6432}
98e87078
RN
6433
6434/**
6435 * Split a track by its segments
0d2b891f 6436 * Routes do not have segments so don't call this for routes
98e87078 6437 */
19782ffd 6438static void trw_layer_split_segments ( menu_array_sublayer values )
98e87078 6439{
19782ffd
RN
6440 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6441 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6442
6443 if ( !trk )
6444 return;
6445
98e87078
RN
6446 guint ntracks;
6447
6448 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6449 gchar *new_tr_name;
6450 guint i;
6451 for ( i = 0; i < ntracks; i++ ) {
6452 if ( tracks[i] ) {
6453 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6454 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
1613e468 6455 g_free ( new_tr_name );
98e87078
RN
6456 }
6457 }
6458 if ( tracks ) {
6459 g_free ( tracks );
6460 // Remove original track
6461 vik_trw_layer_delete_track ( vtl, trk );
da121f9b 6462 vik_layer_emit_update ( VIK_LAYER(vtl) );
98e87078
RN
6463 }
6464 else {
6465 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6466 }
6467}
111fa174
AF
6468/* end of split/merge routines */
6469
a56baa08
RN
6470static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6471{
6472 GList *new_tpl;
6473
6474 // Find available adjacent trackpoint
6475 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6476 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6477 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6478
6479 // Delete current trackpoint
6480 vik_trackpoint_free ( vtl->current_tpl->data );
6481 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6482
6483 // Set to current to the available adjacent trackpoint
6484 vtl->current_tpl = new_tpl;
6485
6486 if ( vtl->current_tp_track ) {
6487 vik_track_calculate_bounds ( vtl->current_tp_track );
6488 }
6489 }
6490 else {
6491 // Delete current trackpoint
6492 vik_trackpoint_free ( vtl->current_tpl->data );
6493 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6494 trw_layer_cancel_current_tp ( vtl, FALSE );
6495 }
6496}
6497
6498/**
6499 * Delete the selected point
6500 */
19782ffd 6501static void trw_layer_delete_point_selected ( menu_array_sublayer values )
a56baa08 6502{
19782ffd 6503 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a56baa08 6504 VikTrack *trk;
19782ffd
RN
6505 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6506 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
a56baa08 6507 else
19782ffd 6508 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
a56baa08
RN
6509
6510 if ( !trk )
6511 return;
6512
6513 if ( !vtl->current_tpl )
6514 return;
6515
6516 trw_layer_trackpoint_selected_delete ( vtl, trk );
6517
6518 // Track has been updated so update tps:
6519 trw_layer_cancel_tps_of_track ( vtl, trk );
6520
6521 vik_layer_emit_update ( VIK_LAYER(vtl) );
6522}
6523
b6eda120
RN
6524/**
6525 * Delete adjacent track points at the same position
6526 * AKA Delete Dulplicates on the Properties Window
6527 */
19782ffd 6528static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
b6eda120 6529{
19782ffd 6530 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6531 VikTrack *trk;
19782ffd
RN
6532 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6533 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6534 else
19782ffd 6535 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6536
6537 if ( !trk )
6538 return;
b6eda120
RN
6539
6540 gulong removed = vik_track_remove_dup_points ( trk );
6541
6542 // Track has been updated so update tps:
6543 trw_layer_cancel_tps_of_track ( vtl, trk );
6544
6545 // Inform user how much was deleted as it's not obvious from the normal view
6546 gchar str[64];
6547 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6548 g_snprintf(str, 64, tmp_str, removed);
6549 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6550
da121f9b 6551 vik_layer_emit_update ( VIK_LAYER(vtl) );
b6eda120
RN
6552}
6553
6579ca1f
RN
6554/**
6555 * Delete adjacent track points with the same timestamp
6556 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6557 */
19782ffd 6558static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6579ca1f 6559{
19782ffd 6560 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6561 VikTrack *trk;
19782ffd
RN
6562 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6563 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6564 else
19782ffd 6565 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6566
6567 if ( !trk )
6568 return;
6579ca1f
RN
6569
6570 gulong removed = vik_track_remove_same_time_points ( trk );
6571
6572 // Track has been updated so update tps:
6573 trw_layer_cancel_tps_of_track ( vtl, trk );
6574
6575 // Inform user how much was deleted as it's not obvious from the normal view
6576 gchar str[64];
6577 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6578 g_snprintf(str, 64, tmp_str, removed);
6579 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6580
da121f9b 6581 vik_layer_emit_update ( VIK_LAYER(vtl) );
6579ca1f
RN
6582}
6583
491794be
RN
6584/**
6585 * Insert a point
6586 */
19782ffd 6587static void trw_layer_insert_point_after ( menu_array_sublayer values )
491794be 6588{
19782ffd 6589 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6590 VikTrack *track;
19782ffd
RN
6591 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6592 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6593 else
19782ffd 6594 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6595
6596 if ( ! track )
6597 return;
6598
6599 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6600
6601 vik_layer_emit_update ( VIK_LAYER(vtl) );
6602}
6603
19782ffd 6604static void trw_layer_insert_point_before ( menu_array_sublayer values )
491794be 6605{
19782ffd 6606 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6607 VikTrack *track;
19782ffd
RN
6608 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6609 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6610 else
19782ffd 6611 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6612
6613 if ( ! track )
6614 return;
6615
6616 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6617
6618 vik_layer_emit_update ( VIK_LAYER(vtl) );
6619}
6620
eb9fd106
RN
6621/**
6622 * Reverse a track
6623 */
19782ffd 6624static void trw_layer_reverse ( menu_array_sublayer values )
eb9fd106 6625{
19782ffd 6626 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6627 VikTrack *track;
19782ffd
RN
6628 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6629 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6630 else
19782ffd 6631 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6632
6633 if ( ! track )
6634 return;
eb9fd106 6635
eb9fd106
RN
6636 vik_track_reverse ( track );
6637
19782ffd 6638 vik_layer_emit_update ( VIK_LAYER(vtl) );
eb9fd106
RN
6639}
6640
be3b5aa8 6641/**
c9e2a471
RN
6642 * Open a program at the specified date
6643 * Mainly for RedNotebook - http://rednotebook.sourceforge.net/
6644 * But could work with any program that accepts a command line of --date=<date>
6645 * FUTURE: Allow configuring of command line options + date format
be3b5aa8
RN
6646 */
6647static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6648{
6649 GError *err = NULL;
c9e2a471 6650 gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str );
be3b5aa8 6651 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
c9e2a471 6652 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program );
be3b5aa8
RN
6653 g_error_free ( err );
6654 }
6655 g_free ( cmd );
6656}
6657
6658/**
6659 * Open a diary at the date of the track or waypoint
6660 */
6661static void trw_layer_diary ( menu_array_sublayer values )
6662{
6663 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6664
6665 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6666 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6667 if ( ! trk )
6668 return;
6669
6670 gchar date_buf[20];
6671 date_buf[0] = '\0';
6672 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6673 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6674 trw_layer_diary_open ( vtl, date_buf );
6675 }
6676 else
6677 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6678 }
6679 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6680 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6681 if ( ! wpt )
6682 return;
6683
6684 gchar date_buf[20];
6685 date_buf[0] = '\0';
6686 if ( wpt->has_timestamp ) {
6687 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6688 trw_layer_diary_open ( vtl, date_buf );
6689 }
6690 else
6691 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6692 }
6693}
6694
e6d91291
RN
6695/**
6696 * Open a program at the specified date
6697 * Mainly for Stellarium - http://stellarium.org/
6698 * But could work with any program that accepts the same command line options...
6699 * FUTURE: Allow configuring of command line options + format or parameters
6700 */
6701static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, const gchar *time_str, const gchar *lat_str, const gchar *lon_str, const gchar *alt_str )
6702{
6703 GError *err = NULL;
6704 gchar *tmp;
6705 g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL );
6706 gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
6707 astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
6708 g_warning ( "%s", cmd );
6709 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6710 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program );
6711 g_warning ( "%s", err->message );
6712 g_error_free ( err );
6713 }
6714 util_add_to_deletion_list ( tmp );
6715 g_free ( tmp );
6716 g_free ( cmd );
6717}
6718
6719// Format of stellarium lat & lon seems designed to be particularly awkward
6720// who uses ' & " in the parameters for the command line?!
6721// -1d4'27.48"
6722// +53d58'16.65"
6723static gchar *convert_to_dms ( gdouble dec )
6724{
6725 gdouble tmp;
6726 gchar sign_c = ' ';
6727 gint val_d, val_m;
6728 gdouble val_s;
6729 gchar *result = NULL;
6730
6731 if ( dec > 0 )
6732 sign_c = '+';
6733 else if ( dec < 0 )
6734 sign_c = '-';
6735 else // Nul value
6736 sign_c = ' ';
6737
6738 // Degrees
6739 tmp = fabs(dec);
6740 val_d = (gint)tmp;
6741
6742 // Minutes
6743 tmp = (tmp - val_d) * 60;
6744 val_m = (gint)tmp;
6745
6746 // Seconds
6747 val_s = (tmp - val_m) * 60;
6748
6749 // Format
6750 result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
6751 return result;
6752}
6753
6754/**
6755 * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
6756 */
6757static void trw_layer_astro ( menu_array_sublayer values )
6758{
6759 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6760
6761 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6762 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6763 if ( ! trk )
6764 return;
6765
6766 VikTrackpoint *tp = NULL;
6767 if ( vtl->current_tpl )
6768 // Current Trackpoint
6769 tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6770 else if ( trk->trackpoints )
6771 // Otherwise first trackpoint
6772 tp = VIK_TRACKPOINT(trk->trackpoints->data);
6773 else
6774 // Give up
6775 return;
6776
6777 if ( tp->has_timestamp ) {
6778 gchar date_buf[20];
6779 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
6780 gchar time_buf[20];
6781 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
6782 struct LatLon ll;
6783 vik_coord_to_latlon ( &tp->coord, &ll );
6784 gchar *lat_str = convert_to_dms ( ll.lat );
6785 gchar *lon_str = convert_to_dms ( ll.lon );
6786 gchar alt_buf[20];
6787 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) );
6788 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf);
6789 g_free ( lat_str );
6790 g_free ( lon_str );
6791 }
6792 else
6793 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6794 }
6795 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6796 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6797 if ( ! wpt )
6798 return;
6799
6800 if ( wpt->has_timestamp ) {
6801 gchar date_buf[20];
6802 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
6803 gchar time_buf[20];
6804 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
6805 struct LatLon ll;
6806 vik_coord_to_latlon ( &wpt->coord, &ll );
6807 gchar *lat_str = convert_to_dms ( ll.lat );
6808 gchar *lon_str = convert_to_dms ( ll.lon );
6809 gchar alt_buf[20];
6810 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) );
6811 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf );
6812 g_free ( lat_str );
6813 g_free ( lon_str );
6814 }
6815 else
6816 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6817 }
6818}
6819
20b671c3
RN
6820/**
6821 * Similar to trw_layer_enum_item, but this uses a sorted method
6822 */
ce4bd1cf 6823/* Currently unused
20b671c3
RN
6824static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6825{
6826 GList **list = (GList**)udata;
ce4bd1cf 6827 // *list = g_list_prepend(*all, key); //unsorted method
20b671c3
RN
6828 // Sort named list alphabetically
6829 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6830}
ce4bd1cf 6831*/
20b671c3 6832
c9570f86
RN
6833/**
6834 * Now Waypoint specific sort
6835 */
6836static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6837{
6838 GList **list = (GList**)udata;
6839 // Sort named list alphabetically
6840 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6841}
6842
ce4bd1cf
RN
6843/**
6844 * Track specific sort
6845 */
6846static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6847{
6848 GList **list = (GList**)udata;
6849 // Sort named list alphabetically
6850 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6851}
6852
3d2ad4f8
RN
6853
6854typedef struct {
6855 gboolean has_same_track_name;
6856 const gchar *same_track_name;
6857} same_track_name_udata;
6858
6859static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6860{
6861 const gchar* namea = (const gchar*) aa;
6862 const gchar* nameb = (const gchar*) bb;
6863
6864 // the test
6865 gint result = strcmp ( namea, nameb );
6866
6867 if ( result == 0 ) {
6868 // Found two names the same
6869 same_track_name_udata *user_data = udata;
6870 user_data->has_same_track_name = TRUE;
6871 user_data->same_track_name = namea;
6872 }
6873
6874 // Leave ordering the same
6875 return 0;
6876}
6877
6878/**
0d2b891f 6879 * Find out if any tracks have the same name in this hash table
3d2ad4f8 6880 */
0d2b891f 6881static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
3d2ad4f8
RN
6882{
6883 // Sort items by name, then compare if any next to each other are the same
6884
6885 GList *track_names = NULL;
0d2b891f 6886 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6887
6888 // No tracks
6889 if ( ! track_names )
6890 return FALSE;
6891
6892 same_track_name_udata udata;
6893 udata.has_same_track_name = FALSE;
6894
6895 // Use sort routine to traverse list comparing items
6896 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6897 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6898 // Still no tracks...
6899 if ( ! dummy_list )
6900 return FALSE;
6901
6902 return udata.has_same_track_name;
6903}
6904
6905/**
0d2b891f 6906 * Force unqiue track names for the track table specified
3d2ad4f8 6907 * Note the panel is a required parameter to enable the update of the names displayed
0d2b891f 6908 * Specify if on tracks or else on routes
3d2ad4f8 6909 */
0d2b891f 6910static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
3d2ad4f8
RN
6911{
6912 // . Search list for an instance of repeated name
6913 // . get track of this name
6914 // . create new name
6915 // . rename track & update equiv. treeview iter
6916 // . repeat until all different
6917
6918 same_track_name_udata udata;
6919
6920 GList *track_names = NULL;
6921 udata.has_same_track_name = FALSE;
6922 udata.same_track_name = NULL;
6923
0d2b891f 6924 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6925
6926 // No tracks
6927 if ( ! track_names )
6928 return;
6929
6930 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6931
6932 // Still no tracks...
6933 if ( ! dummy_list1 )
6934 return;
6935
6936 while ( udata.has_same_track_name ) {
6937
6938 // Find a track with the same name
0d2b891f
RN
6939 VikTrack *trk;
6940 if ( ontrack )
6941 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6942 else
6943 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
3d2ad4f8
RN
6944
6945 if ( ! trk ) {
6946 // Broken :(
6947 g_critical("Houston, we've had a problem.");
6948 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6949 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6950 return;
6951 }
6952
6953 // Rename it
9748531a 6954 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
3d2ad4f8
RN
6955 vik_track_set_name ( trk, newname );
6956
6957 trku_udata udataU;
6958 udataU.trk = trk;
6959 udataU.uuid = NULL;
6960
6961 // Need want key of it for treeview update
0d2b891f 6962 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
3d2ad4f8
RN
6963
6964 if ( trkf && udataU.uuid ) {
6965
0d2b891f
RN
6966 GtkTreeIter *it;
6967 if ( ontrack )
6968 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6969 else
6970 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
3d2ad4f8
RN
6971
6972 if ( it ) {
6973 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
c9cac058
RN
6974 if ( ontrack )
6975 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6976 else
6977 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
3d2ad4f8
RN
6978 }
6979 }
6980
6981 // Start trying to find same names again...
6982 track_names = NULL;
0d2b891f 6983 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6984 udata.has_same_track_name = FALSE;
6985 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6986
6987 // No tracks any more - give up searching
6988 if ( ! dummy_list2 )
6989 udata.has_same_track_name = FALSE;
6990 }
6991
6992 // Update
6993 vik_layers_panel_emit_update ( vlp );
6994}
6995
19782ffd 6996static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
c9cac058 6997{
19782ffd 6998 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6999 GtkTreeIter *iter;
7000
19782ffd 7001 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
7002 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
7003 iter = &(vtl->tracks_iter);
7004 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
7005 break;
7006 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7007 iter = &(vtl->routes_iter);
7008 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
7009 break;
7010 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7011 iter = &(vtl->waypoints_iter);
7012 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
7013 break;
7014 }
7015
7016 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
7017}
7018
19782ffd 7019static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
c9cac058 7020{
19782ffd 7021 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
7022 GtkTreeIter *iter;
7023
19782ffd 7024 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
7025 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
7026 iter = &(vtl->tracks_iter);
7027 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
7028 break;
7029 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7030 iter = &(vtl->routes_iter);
7031 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
7032 break;
7033 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7034 iter = &(vtl->waypoints_iter);
7035 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
7036 break;
7037 }
7038
7039 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
7040}
7041
20b671c3
RN
7042/**
7043 *
7044 */
1c2083ba 7045static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
20b671c3 7046{
1c2083ba 7047 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3 7048 GList *all = NULL;
ce4bd1cf 7049
3d2ad4f8 7050 // Ensure list of track names offered is unique
0d2b891f 7051 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
3d2ad4f8
RN
7052 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7053 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 7054 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
3d2ad4f8
RN
7055 }
7056 else
7057 return;
7058 }
ce4bd1cf 7059
20b671c3 7060 // Sort list alphabetically for better presentation
ce4bd1cf 7061 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
20b671c3
RN
7062
7063 if ( ! all ) {
7064 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
7065 return;
7066 }
7067
7068 // Get list of items to delete from the user
7069 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7070 all,
7071 TRUE,
7072 _("Delete Selection"),
7073 _("Select tracks to delete"));
7074 g_list_free(all);
7075
7076 // Delete requested tracks
7077 // since specificly requested, IMHO no need for extra confirmation
7078 if ( delete_list ) {
7079 GList *l;
7080 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3d2ad4f8 7081 // This deletes first trk it finds of that name (but uniqueness is enforced above)
0d2b891f
RN
7082 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
7083 }
7084 g_list_free(delete_list);
da121f9b 7085 vik_layer_emit_update( VIK_LAYER(vtl) );
0d2b891f
RN
7086 }
7087}
7088
7089/**
7090 *
7091 */
1c2083ba 7092static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
0d2b891f 7093{
1c2083ba 7094 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
7095 GList *all = NULL;
7096
7097 // Ensure list of track names offered is unique
7098 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
7099 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7100 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 7101 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
0d2b891f
RN
7102 }
7103 else
7104 return;
7105 }
7106
7107 // Sort list alphabetically for better presentation
7108 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7109
7110 if ( ! all ) {
7111 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
7112 return;
7113 }
7114
7115 // Get list of items to delete from the user
7116 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7117 all,
7118 TRUE,
7119 _("Delete Selection"),
7120 _("Select routes to delete") );
7121 g_list_free(all);
7122
7123 // Delete requested routes
7124 // since specificly requested, IMHO no need for extra confirmation
7125 if ( delete_list ) {
7126 GList *l;
7127 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7128 // This deletes first route it finds of that name (but uniqueness is enforced above)
7129 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
20b671c3
RN
7130 }
7131 g_list_free(delete_list);
da121f9b 7132 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
7133 }
7134}
7135
33a89785
RN
7136typedef struct {
7137 gboolean has_same_waypoint_name;
7138 const gchar *same_waypoint_name;
7139} same_waypoint_name_udata;
7140
7141static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
7142{
7143 const gchar* namea = (const gchar*) aa;
7144 const gchar* nameb = (const gchar*) bb;
7145
7146 // the test
7147 gint result = strcmp ( namea, nameb );
7148
7149 if ( result == 0 ) {
7150 // Found two names the same
7151 same_waypoint_name_udata *user_data = udata;
7152 user_data->has_same_waypoint_name = TRUE;
7153 user_data->same_waypoint_name = namea;
7154 }
7155
7156 // Leave ordering the same
7157 return 0;
7158}
7159
7160/**
7161 * Find out if any waypoints have the same name in this layer
7162 */
7163gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
7164{
7165 // Sort items by name, then compare if any next to each other are the same
7166
7167 GList *waypoint_names = NULL;
7168 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7169
7170 // No waypoints
7171 if ( ! waypoint_names )
7172 return FALSE;
7173
7174 same_waypoint_name_udata udata;
7175 udata.has_same_waypoint_name = FALSE;
7176
7177 // Use sort routine to traverse list comparing items
7178 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7179 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7180 // Still no waypoints...
7181 if ( ! dummy_list )
7182 return FALSE;
7183
7184 return udata.has_same_waypoint_name;
7185}
7186
7187/**
7188 * Force unqiue waypoint names for this layer
7189 * Note the panel is a required parameter to enable the update of the names displayed
7190 */
7191static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7192{
7193 // . Search list for an instance of repeated name
7194 // . get waypoint of this name
7195 // . create new name
7196 // . rename waypoint & update equiv. treeview iter
7197 // . repeat until all different
7198
7199 same_waypoint_name_udata udata;
7200
7201 GList *waypoint_names = NULL;
7202 udata.has_same_waypoint_name = FALSE;
7203 udata.same_waypoint_name = NULL;
7204
7205 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7206
7207 // No waypoints
7208 if ( ! waypoint_names )
7209 return;
7210
7211 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7212
7213 // Still no waypoints...
7214 if ( ! dummy_list1 )
7215 return;
7216
7217 while ( udata.has_same_waypoint_name ) {
7218
7219 // Find a waypoint with the same name
7220 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7221
7222 if ( ! waypoint ) {
7223 // Broken :(
7224 g_critical("Houston, we've had a problem.");
7225 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7226 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7227 return;
7228 }
7229
7230 // Rename it
9748531a 7231 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
33a89785 7232
6761d8a4 7233 trw_layer_waypoint_rename ( vtl, waypoint, newname );
33a89785
RN
7234
7235 // Start trying to find same names again...
7236 waypoint_names = NULL;
7237 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7238 udata.has_same_waypoint_name = FALSE;
7239 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7240
7241 // No waypoints any more - give up searching
7242 if ( ! dummy_list2 )
7243 udata.has_same_waypoint_name = FALSE;
7244 }
7245
7246 // Update
7247 vik_layers_panel_emit_update ( vlp );
7248}
7249
20b671c3
RN
7250/**
7251 *
7252 */
1c2083ba 7253static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
20b671c3 7254{
1c2083ba 7255 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3
RN
7256 GList *all = NULL;
7257
33a89785
RN
7258 // Ensure list of waypoint names offered is unique
7259 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7260 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7261 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 7262 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
33a89785
RN
7263 }
7264 else
7265 return;
7266 }
c9570f86 7267
20b671c3 7268 // Sort list alphabetically for better presentation
c9570f86 7269 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
20b671c3
RN
7270 if ( ! all ) {
7271 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7272 return;
7273 }
7274
7275 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7276
7277 // Get list of items to delete from the user
7278 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7279 all,
7280 TRUE,
7281 _("Delete Selection"),
7282 _("Select waypoints to delete"));
7283 g_list_free(all);
7284
7285 // Delete requested waypoints
7286 // since specificly requested, IMHO no need for extra confirmation
7287 if ( delete_list ) {
7288 GList *l;
7289 for (l = delete_list; l != NULL; l = g_list_next(l)) {
33a89785 7290 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
c9570f86 7291 trw_layer_delete_waypoint_by_name (vtl, l->data);
20b671c3
RN
7292 }
7293 g_list_free(delete_list);
aa0665e5
RN
7294
7295 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 7296 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
7297 }
7298
7299}
111fa174 7300
89fdc417
RN
7301/**
7302 *
7303 */
7304static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7305{
7306 vik_treeview_item_toggle_visible ( vt, it );
7307}
7308
7309/**
7310 *
7311 */
7312static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7313{
7314 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7315}
7316
7317/**
7318 *
7319 */
7320static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7321{
7322 wp->visible = GPOINTER_TO_INT (on_off);
7323}
7324
7325/**
7326 *
7327 */
7328static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7329{
7330 wp->visible = !wp->visible;
7331}
7332
7333/**
7334 *
7335 */
1c2083ba 7336static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
89fdc417 7337{
1c2083ba 7338 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7339 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7340 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7341 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7342 // Redraw
7343 vik_layer_emit_update ( VIK_LAYER(vtl) );
7344}
7345
7346/**
7347 *
7348 */
1c2083ba 7349static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
89fdc417 7350{
1c2083ba 7351 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7352 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7353 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7354 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7355 // Redraw
7356 vik_layer_emit_update ( VIK_LAYER(vtl) );
7357}
7358
7359/**
7360 *
7361 */
1c2083ba 7362static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
89fdc417 7363{
1c2083ba 7364 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7365 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7366 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7367 // Redraw
7368 vik_layer_emit_update ( VIK_LAYER(vtl) );
7369}
7370
7371/**
7372 *
7373 */
7374static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7375{
7376 trk->visible = GPOINTER_TO_INT (on_off);
7377}
7378
7379/**
7380 *
7381 */
7382static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7383{
7384 trk->visible = !trk->visible;
7385}
7386
7387/**
7388 *
7389 */
1c2083ba 7390static void trw_layer_tracks_visibility_off ( menu_array_layer values )
89fdc417 7391{
1c2083ba 7392 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7393 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7394 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7395 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7396 // Redraw
7397 vik_layer_emit_update ( VIK_LAYER(vtl) );
7398}
7399
7400/**
7401 *
7402 */
1c2083ba 7403static void trw_layer_tracks_visibility_on ( menu_array_layer values )
89fdc417 7404{
1c2083ba 7405 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7406 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7407 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7408 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7409 // Redraw
7410 vik_layer_emit_update ( VIK_LAYER(vtl) );
7411}
7412
7413/**
7414 *
7415 */
1c2083ba 7416static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
89fdc417 7417{
1c2083ba 7418 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7419 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7420 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7421 // Redraw
7422 vik_layer_emit_update ( VIK_LAYER(vtl) );
7423}
7424
7425/**
7426 *
7427 */
1c2083ba 7428static void trw_layer_routes_visibility_off ( menu_array_layer values )
89fdc417 7429{
1c2083ba 7430 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7431 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7432 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7433 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7434 // Redraw
7435 vik_layer_emit_update ( VIK_LAYER(vtl) );
7436}
7437
7438/**
7439 *
7440 */
1c2083ba 7441static void trw_layer_routes_visibility_on ( menu_array_layer values )
89fdc417 7442{
1c2083ba 7443 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7444 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7445 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7446 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7447 // Redraw
7448 vik_layer_emit_update ( VIK_LAYER(vtl) );
7449}
7450
7451/**
7452 *
7453 */
1c2083ba 7454static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
89fdc417 7455{
1c2083ba 7456 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7457 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7458 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7459 // Redraw
7460 vik_layer_emit_update ( VIK_LAYER(vtl) );
7461}
7462
00176e85
RN
7463/**
7464 * vik_trw_layer_build_waypoint_list_t:
7465 *
7466 * Helper function to construct a list of #vik_trw_waypoint_list_t
7467 */
7468GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7469{
7470 GList *waypoints_and_layers = NULL;
7471 // build waypoints_and_layers list
7472 while ( waypoints ) {
7473 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7474 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7475 vtdl->vtl = vtl;
7476 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7477 waypoints = g_list_next ( waypoints );
7478 }
7479 return waypoints_and_layers;
7480}
7481
7482/**
7483 * trw_layer_create_waypoint_list:
7484 *
7485 * Create the latest list of waypoints with the associated layer(s)
7486 * Although this will always be from a single layer here
7487 */
7488static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7489{
7490 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7491 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7492
7493 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7494}
7495
43984ba1
RN
7496/**
7497 * trw_layer_analyse_close:
7498 *
7499 * Stuff to do on dialog closure
7500 */
7501static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7502{
7503 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7504 gtk_widget_destroy ( dialog );
7505 vtl->tracks_analysis_dialog = NULL;
7506}
7507
7508/**
184ec0fa 7509 * vik_trw_layer_build_track_list_t:
43984ba1 7510 *
184ec0fa 7511 * Helper function to construct a list of #vik_trw_track_list_t
43984ba1 7512 */
184ec0fa 7513GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
43984ba1 7514{
43984ba1
RN
7515 GList *tracks_and_layers = NULL;
7516 // build tracks_and_layers list
43984ba1
RN
7517 while ( tracks ) {
7518 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7519 vtdl->trk = VIK_TRACK(tracks->data);
7520 vtdl->vtl = vtl;
7521 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7522 tracks = g_list_next ( tracks );
7523 }
43984ba1
RN
7524 return tracks_and_layers;
7525}
7526
184ec0fa
RN
7527/**
7528 * trw_layer_create_track_list:
7529 *
7530 * Create the latest list of tracks with the associated layer(s)
7531 * Although this will always be from a single layer here
7532 */
7533static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7534{
7535 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7536 GList *tracks = NULL;
7537 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7538 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7539 else
7540 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7541
7542 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7543}
7544
1c2083ba 7545static void trw_layer_tracks_stats ( menu_array_layer values )
43984ba1 7546{
1c2083ba 7547 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7548 // There can only be one!
7549 if ( vtl->tracks_analysis_dialog )
7550 return;
7551
7552 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7553 VIK_LAYER(vtl)->name,
7554 VIK_LAYER(vtl),
7555 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
184ec0fa 7556 trw_layer_create_track_list,
43984ba1
RN
7557 trw_layer_analyse_close );
7558}
7559
7560/**
7561 *
7562 */
1c2083ba 7563static void trw_layer_routes_stats ( menu_array_layer values )
43984ba1 7564{
1c2083ba 7565 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7566 // There can only be one!
7567 if ( vtl->tracks_analysis_dialog )
7568 return;
7569
7570 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7571 VIK_LAYER(vtl)->name,
7572 VIK_LAYER(vtl),
7573 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
184ec0fa 7574 trw_layer_create_track_list,
43984ba1
RN
7575 trw_layer_analyse_close );
7576}
7577
19782ffd 7578static void trw_layer_goto_waypoint ( menu_array_sublayer values )
50a14534 7579{
19782ffd
RN
7580 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7581 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
50a14534 7582 if ( wp )
19782ffd 7583 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
50a14534
EB
7584}
7585
19782ffd 7586static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
50a14534 7587{
19782ffd
RN
7588 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7589 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
d64e2724
RN
7590 if ( !wp )
7591 return;
7592 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
19782ffd 7593 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
50a14534
EB
7594 g_free ( webpage );
7595}
7596
19782ffd 7597static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
3c13fff8 7598{
19782ffd
RN
7599 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7600 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3c13fff8
RN
7601 if ( !wp )
7602 return;
415b0e21
RN
7603 if ( wp->url ) {
7604 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7605 } else if ( !strncmp(wp->comment, "http", 4) ) {
19782ffd 7606 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
3c13fff8 7607 } else if ( !strncmp(wp->description, "http", 4) ) {
19782ffd 7608 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
3c13fff8
RN
7609 }
7610}
7611
a7cd93ac 7612static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
50a14534
EB
7613{
7614 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7615 {
c9570f86 7616 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
50a14534 7617
c9570f86 7618 // No actual change to the name supplied
27d267f4
RN
7619 if ( wp->name )
7620 if (strcmp(newname, wp->name) == 0 )
7621 return NULL;
50a14534 7622
c9570f86
RN
7623 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7624
7625 if ( wpf ) {
7626 // An existing waypoint has been found with the requested name
7627 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7628 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
c9570f86 7629 newname ) )
8499a412 7630 return NULL;
50a14534
EB
7631 }
7632
c9570f86
RN
7633 // Update WP name and refresh the treeview
7634 vik_waypoint_set_name (wp, newname);
50a14534 7635
c9cac058
RN
7636 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7637 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
50a14534
EB
7638
7639 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
c9570f86
RN
7640
7641 return newname;
50a14534 7642 }
c9570f86 7643
50a14534
EB
7644 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7645 {
ce4bd1cf 7646 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
50a14534 7647
ce4bd1cf 7648 // No actual change to the name supplied
27d267f4
RN
7649 if ( trk->name )
7650 if (strcmp(newname, trk->name) == 0)
7651 return NULL;
50a14534 7652
ce4bd1cf
RN
7653 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7654
7655 if ( trkf ) {
7656 // An existing track has been found with the requested name
7657 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7658 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
ce4bd1cf 7659 newname ) )
8499a412 7660 return NULL;
50a14534 7661 }
ce4bd1cf
RN
7662 // Update track name and refresh GUI parts
7663 vik_track_set_name (trk, newname);
50a14534 7664
ce4bd1cf
RN
7665 // Update any subwindows that could be displaying this track which has changed name
7666 // Only one Track Edit Window
7667 if ( l->current_tp_track == trk && l->tpwin ) {
7668 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
50a14534 7669 }
ce4bd1cf
RN
7670 // Property Dialog of the track
7671 vik_trw_layer_propwin_update ( trk );
50a14534 7672
c9cac058
RN
7673 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7674 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
0d2b891f
RN
7675
7676 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7677
7678 return newname;
7679 }
7680
7681 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7682 {
7683 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7684
7685 // No actual change to the name supplied
27d267f4
RN
7686 if ( trk->name )
7687 if (strcmp(newname, trk->name) == 0)
7688 return NULL;
0d2b891f
RN
7689
7690 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7691
7692 if ( trkf ) {
7693 // An existing track has been found with the requested name
7694 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7695 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7696 newname ) )
7697 return NULL;
7698 }
7699 // Update track name and refresh GUI parts
7700 vik_track_set_name (trk, newname);
7701
7702 // Update any subwindows that could be displaying this track which has changed name
7703 // Only one Track Edit Window
7704 if ( l->current_tp_track == trk && l->tpwin ) {
7705 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7706 }
7707 // Property Dialog of the track
7708 vik_trw_layer_propwin_update ( trk );
7709
c9cac058
RN
7710 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7711 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
50a14534
EB
7712
7713 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
ce4bd1cf
RN
7714
7715 return newname;
50a14534
EB
7716 }
7717 return NULL;
7718}
7719
7720static gboolean is_valid_geocache_name ( gchar *str )
7721{
7722 gint len = strlen ( str );
0c1044e9 7723 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
7724}
7725
19782ffd 7726static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
28c82d8b 7727{
19782ffd 7728 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 7729 a_acquire_set_filter_track ( trk );
28c82d8b
EB
7730}
7731
55340efa 7732#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 7733static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
bddd2056 7734{
008e972c 7735 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
bddd2056
EB
7736 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7737}
7738
19782ffd 7739static void trw_layer_google_route_webpage ( menu_array_sublayer values )
bddd2056 7740{
19782ffd 7741 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
bddd2056
EB
7742 if ( tr ) {
7743 gchar *escaped = uri_escape ( tr->comment );
7744 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
19782ffd 7745 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
bddd2056 7746 g_free ( escaped );
bddd2056
EB
7747 g_free ( webpage );
7748 }
7749}
ff1f2b3e 7750#endif
bddd2056 7751
50eadc64
RN
7752/* vlp can be NULL if necessary - i.e. right-click from a tool */
7753/* viewpoint is now available instead */
a7cd93ac 7754static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534 7755{
19782ffd 7756 static menu_array_sublayer pass_along;
50a14534
EB
7757 GtkWidget *item;
7758 gboolean rv = FALSE;
7759
19782ffd
RN
7760 pass_along[MA_VTL] = l;
7761 pass_along[MA_VLP] = vlp;
7762 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7763 pass_along[MA_SUBLAYER_ID] = sublayer;
7764 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7765 pass_along[MA_VVP] = vvp;
7766 pass_along[MA_TV_ITER] = iter;
7767 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
50a14534 7768
0d2b891f 7769 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7770 {
7771 rv = TRUE;
7772
7773 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7775 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7776 gtk_widget_show ( item );
7777
21700912 7778 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
0d2b891f
RN
7779 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7780 if (tr && tr->property_dialog)
7781 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7782 }
7783 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7784 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
21700912
QT
7785 if (tr && tr->property_dialog)
7786 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7787 }
7788
2cebc318
QT
7789 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7790 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7791 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7792 gtk_widget_show ( item );
7793
7794 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7795 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7796 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7797 gtk_widget_show ( item );
7798
50a14534
EB
7799 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7800 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7801 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7802 gtk_widget_show ( item );
7803
7804 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7805 {
a8952eb1
RN
7806 // Always create separator as now there is always at least the transform menu option
7807 item = gtk_menu_item_new ();
7808 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7809 gtk_widget_show ( item );
5ede6aa6 7810
50a14534
EB
7811 /* could be a right-click using the tool */
7812 if ( vlp != NULL ) {
d6de71f9 7813 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
a8952eb1 7814 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
7815 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7816 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7817 gtk_widget_show ( item );
7818 }
7819
c9570f86 7820 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5ede6aa6 7821
c9570f86
RN
7822 if ( wp && wp->name ) {
7823 if ( is_valid_geocache_name ( wp->name ) ) {
c9570f86
RN
7824 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7825 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7826 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7827 gtk_widget_show ( item );
7828 }
41f4abac
RN
7829#ifdef VIK_CONFIG_GEOTAG
7830 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7832 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7833 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7834 gtk_widget_show ( item );
7835#endif
c9570f86 7836 }
a412f3f5
RN
7837
7838 if ( wp && wp->image )
7839 {
19782ffd
RN
7840 // Set up image paramater
7841 pass_along[MA_MISC] = wp->image;
a412f3f5 7842
d6de71f9 7843 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
43b5e335 7844 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
7845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7846 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7847 gtk_widget_show ( item );
b3eb3b98
RN
7848
7849#ifdef VIK_CONFIG_GEOTAG
7850 GtkWidget *geotag_submenu = gtk_menu_new ();
7851 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7852 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7853 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7854 gtk_widget_show ( item );
7855 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7856
7857 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7859 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7860 gtk_widget_show ( item );
7861
7862 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7863 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7864 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7865 gtk_widget_show ( item );
7866#endif
a412f3f5
RN
7867 }
7868
3c13fff8
RN
7869 if ( wp )
7870 {
415b0e21
RN
7871 if ( wp->url ||
7872 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
3c13fff8
RN
7873 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7874 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7877 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7878 gtk_widget_show ( item );
7879 }
7880 }
50a14534
EB
7881 }
7882 }
7883
0d2b891f 7884 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d80642e
RN
7885 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7886 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7887 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7888 gtk_widget_show ( item );
7889 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7890 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7891 gtk_widget_set_sensitive ( item, TRUE );
7892 else
7893 gtk_widget_set_sensitive ( item, FALSE );
7894
7895 // Add separator
7896 item = gtk_menu_item_new ();
7897 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7898 gtk_widget_show ( item );
7899 }
7900
5ede6aa6
RN
7901 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7902 {
b66bb4ab 7903 rv = TRUE;
d6de71f9
RN
7904 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7905 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5ede6aa6
RN
7906 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7907 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7908 gtk_widget_show ( item );
7909 }
7910
539ba038
RN
7911 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7912 {
d6de71f9
RN
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
539ba038
RN
7915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7916 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7917 gtk_widget_show ( item );
7918
d6de71f9
RN
7919 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7920 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
539ba038
RN
7921 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7922 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7923 gtk_widget_show ( item );
c9a5cbf9 7924
d6de71f9
RN
7925 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7926 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7929 gtk_widget_show ( item );
20b671c3 7930
d6de71f9
RN
7931 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7932 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7934 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7935 gtk_widget_show ( item );
89fdc417
RN
7936
7937 GtkWidget *vis_submenu = gtk_menu_new ();
7938 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
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), vis_submenu );
7942
7943 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7944 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7947 gtk_widget_show ( item );
7948
7949 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7950 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7952 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7953 gtk_widget_show ( item );
7954
7955 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7956 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7958 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7959 gtk_widget_show ( item );
00176e85
RN
7960
7961 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7962 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7964 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
539ba038
RN
7965 }
7966
f1e68516
RN
7967 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7968 {
7969 rv = TRUE;
7970
0d2b891f 7971 if ( l->current_track && !l->current_track->is_route ) {
37615c52
RN
7972 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7974 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7975 gtk_widget_show ( item );
7976 // Add separator
7977 item = gtk_menu_item_new ();
7978 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7979 gtk_widget_show ( item );
7980 }
7981
d6de71f9
RN
7982 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7983 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
f1e68516
RN
7984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7985 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7986 gtk_widget_show ( item );
c9a5cbf9 7987
37615c52
RN
7988 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7989 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7991 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7992 gtk_widget_show ( item );
7993 // Make it available only when a new track *not* already in progress
7994 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7995
d6de71f9
RN
7996 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7997 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7999 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8000 gtk_widget_show ( item );
20b671c3 8001
d6de71f9
RN
8002 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
8003 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
8004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
8005 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8006 gtk_widget_show ( item );
89fdc417
RN
8007
8008 GtkWidget *vis_submenu = gtk_menu_new ();
8009 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8010 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8011 gtk_widget_show ( item );
8012 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8013
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
8015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8016 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
8017 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8018 gtk_widget_show ( item );
8019
8020 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
8021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
8023 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8024 gtk_widget_show ( item );
8025
8026 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
8029 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
8030
8031 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
8032 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8034 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
89fdc417 8035 gtk_widget_show ( item );
43984ba1
RN
8036
8037 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
8039 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8040 gtk_widget_show ( item );
f1e68516
RN
8041 }
8042
0d2b891f
RN
8043 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
8044 {
8045 rv = TRUE;
8046
8047 if ( l->current_track && l->current_track->is_route ) {
8048 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8049 // Reuse finish track method
8050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8051 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8052 gtk_widget_show ( item );
8053 // Add separator
8054 item = gtk_menu_item_new ();
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8056 gtk_widget_show ( item );
8057 }
8058
8059 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
8060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
8062 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8063 gtk_widget_show ( item );
8064
8065 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
8066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
8068 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8069 gtk_widget_show ( item );
8070 // Make it available only when a new track *not* already in progress
8071 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8072
8073 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
8074 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
8076 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8077 gtk_widget_show ( item );
8078
8079 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
8080 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
8082 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8083 gtk_widget_show ( item );
89fdc417
RN
8084
8085 GtkWidget *vis_submenu = gtk_menu_new ();
8086 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8087 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8088 gtk_widget_show ( item );
8089 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8090
8091 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
8092 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
8094 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8095 gtk_widget_show ( item );
8096
8097 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
8098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
8100 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8101 gtk_widget_show ( item );
8102
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
8106 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
8107
8108 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
8109 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8110 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8111 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8112
89fdc417 8113 gtk_widget_show ( item );
43984ba1
RN
8114
8115 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8116 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
8117 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8118 gtk_widget_show ( item );
0d2b891f
RN
8119 }
8120
c9cac058
RN
8121
8122 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8123 GtkWidget *submenu_sort = gtk_menu_new ();
8124 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
8125 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8126 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8127 gtk_widget_show ( item );
8128 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
8129
8130 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
8131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
8133 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8134 gtk_widget_show ( item );
8135
8136 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
8137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
8139 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8140 gtk_widget_show ( item );
8141 }
8142
0d2b891f
RN
8143 GtkWidget *upload_submenu = gtk_menu_new ();
8144
8145 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
8146 {
8147 item = gtk_menu_item_new ();
8148 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8149 gtk_widget_show ( item );
8150
0d2b891f 8151 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
37615c52 8152 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
0d2b891f
RN
8153 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
8154 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8155 if ( l->current_track ) {
37615c52
RN
8156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8157 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8158 gtk_widget_show ( item );
8159
8160 // Add separator
8161 item = gtk_menu_item_new ();
8162 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8163 gtk_widget_show ( item );
8164 }
8165
0d2b891f
RN
8166 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8167 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
8168 else
8169 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
88a49424
RN
8170 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8172 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8173 gtk_widget_show ( item );
8174
626de648
RN
8175 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8177 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8178 gtk_widget_show ( item );
8179
88a49424 8180 GtkWidget *goto_submenu;
937b36ed 8181 goto_submenu = gtk_menu_new ();
d6de71f9
RN
8182 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8183 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
937b36ed
RN
8184 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8185 gtk_widget_show ( item );
8186 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8187
d6de71f9
RN
8188 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8189 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
50a14534 8190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 8191 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
8192 gtk_widget_show ( item );
8193
d6de71f9
RN
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534 8196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 8197 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
8198 gtk_widget_show ( item );
8199
d6de71f9
RN
8200 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8201 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
50a14534 8202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 8203 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 8204 gtk_widget_show ( item );
111fa174 8205
d6de71f9
RN
8206 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
c28faca8
RN
8208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8209 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8210 gtk_widget_show ( item );
8211
d6de71f9
RN
8212 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
c28faca8
RN
8214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8215 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8216 gtk_widget_show ( item );
8217
0d2b891f
RN
8218 // Routes don't have speeds
8219 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8220 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8223 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8224 gtk_widget_show ( item );
8225 }
03e7da75 8226
59f9414d
RN
8227 GtkWidget *combine_submenu;
8228 combine_submenu = gtk_menu_new ();
8229 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8230 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8231 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
469113fb 8232 gtk_widget_show ( item );
59f9414d 8233 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
469113fb 8234
0d2b891f
RN
8235 // Routes don't have times or segments...
8236 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8237 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8238 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8239 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8240 gtk_widget_show ( item );
8241
8242 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8244 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8245 gtk_widget_show ( item );
8246 }
111fa174 8247
40a68e7c 8248 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab 8249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
59f9414d 8250 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
291edcab
HR
8251 gtk_widget_show ( item );
8252
0d2b891f
RN
8253 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8254 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8255 else
8256 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
9c34f614
RN
8257 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8258 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8259 gtk_widget_show ( item );
8260
6b5b6c47
RN
8261 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8262 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8263 else
8264 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8266 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8267 gtk_widget_show ( item );
8268
78ac928c
RN
8269 GtkWidget *split_submenu;
8270 split_submenu = gtk_menu_new ();
8271 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8272 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8273 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
291edcab 8274 gtk_widget_show ( item );
78ac928c 8275 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
291edcab 8276
0d2b891f
RN
8277 // Routes don't have times or segments...
8278 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8279 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8281 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8282 gtk_widget_show ( item );
8283
8284 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8285 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8287 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8288 gtk_widget_show ( item );
8289 }
7114e879 8290
7306a492 8291 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
af2341f3 8292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
78ac928c 8293 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
af2341f3
RN
8294 gtk_widget_show ( item );
8295
a4aefe69
RN
8296 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8297 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8298 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8299 gtk_widget_show ( item );
8300 // Make it available only when a trackpoint is selected.
8301 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8302
491794be
RN
8303 GtkWidget *insert_submenu = gtk_menu_new ();
8304 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8306 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8307 gtk_widget_show ( item );
8308 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8309
8310 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8312 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8313 gtk_widget_show ( item );
8314 // Make it available only when a point is selected
8315 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8316
8317 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8318 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8319 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8320 gtk_widget_show ( item );
8321 // Make it available only when a point is selected
8322 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8323
b6eda120
RN
8324 GtkWidget *delete_submenu;
8325 delete_submenu = gtk_menu_new ();
8326 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8327 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8329 gtk_widget_show ( item );
8330 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8331
a56baa08
RN
8332 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8335 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8336 gtk_widget_show ( item );
8337 // Make it available only when a point is selected
8338 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8339
b6eda120
RN
8340 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8341 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8342 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8343 gtk_widget_show ( item );
8344
6579ca1f
RN
8345 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8346 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8347 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
af2341f3
RN
8348 gtk_widget_show ( item );
8349
89d310d1
RN
8350 GtkWidget *transform_submenu;
8351 transform_submenu = gtk_menu_new ();
8352 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8353 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8354 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8355 gtk_widget_show ( item );
8356 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8357
4d333042
RN
8358 GtkWidget *dem_submenu;
8359 dem_submenu = gtk_menu_new ();
89d310d1
RN
8360 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8361 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 8362 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
4d333042
RN
8363 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8364
8365 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8367 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8368 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8369 gtk_widget_show ( item );
8370
8371 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8372 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8373 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8374 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
89d310d1
RN
8375 gtk_widget_show ( item );
8376
81ac2835
RN
8377 GtkWidget *smooth_submenu;
8378 smooth_submenu = gtk_menu_new ();
8379 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8380 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8381 gtk_widget_show ( item );
8382 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8383
8384 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8386 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8387 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8388 gtk_widget_show ( item );
8389
8390 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8392 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8393 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8394 gtk_widget_show ( item );
8395
89d310d1
RN
8396 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8397 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8398 else
8399 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8400 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8401 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8402 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8403 gtk_widget_show ( item );
8404
76b14439
RN
8405 // Routes don't have timestamps - so this is only available for tracks
8406 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8407 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8409 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8410 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8411 gtk_widget_show ( item );
8412 }
8413
0d2b891f
RN
8414 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8415 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8416 else
8417 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
eb9fd106
RN
8418 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8420 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8421 gtk_widget_show ( item );
8422
1a3be6a8
GB
8423 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8424 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8425 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8426 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8427 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8428 gtk_widget_show ( item );
8429 }
8430
6bb72350
RN
8431 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8432 if ( vlp ) {
0d2b891f
RN
8433 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8434 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8435 else
8436 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
43b5e335 8437 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
8438 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8439 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8440 gtk_widget_show ( item );
8441 }
ad0a8c2d 8442
0d2b891f
RN
8443 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8444 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8445 else
8446 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
d6de71f9 8447 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7f6757c4
RN
8448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8449 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8450 gtk_widget_show ( item );
8451
0d2b891f
RN
8452 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8453 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8454 else
8455 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
d6de71f9 8456 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8fb71d6c
EB
8457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8458 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 8459 gtk_widget_show ( item );
5092de80 8460
008e972c
RN
8461 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8462 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
43b5e335 8463 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
8464 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8465 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8466 gtk_widget_show ( item );
8467 }
a7955c1d 8468
0d2b891f
RN
8469 // ATM can't upload a single waypoint but can do waypoints to a GPS
8470 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8471 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8472 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8473 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8474 gtk_widget_show ( item );
8475 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8476
8477 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8478 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8480 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8481 gtk_widget_show ( item );
8482 }
8483 }
e50758c7 8484
be3b5aa8 8485 // Only made available if a suitable program is installed
e6d91291
RN
8486 if ( (have_astro_program || have_diary_program) &&
8487 (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
8488 GtkWidget *external_submenu;
8489 external_submenu = gtk_menu_new ();
8490 item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
8491 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
8492 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8493 gtk_widget_show ( item );
8494 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
8495
8496 if ( have_diary_program ) {
8497 item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
be3b5aa8
RN
8498 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8499 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
e6d91291
RN
8500 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8501 gtk_widget_show ( item );
8502 }
8503
8504 if ( have_astro_program ) {
8505 item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
8506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
8507 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
be3b5aa8
RN
8508 gtk_widget_show ( item );
8509 }
8510 }
8511
008e972c
RN
8512#ifdef VIK_CONFIG_GOOGLE
8513 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8514 {
8515 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8516 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8517 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8518 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8519 gtk_widget_show ( item );
8520 }
8521#endif
8522
0d2b891f
RN
8523 // Some things aren't usable with routes
8524 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
5092de80 8525#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9 8526 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
d6c110f1
RN
8527 // Convert internal pointer into track
8528 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
d6de71f9 8529 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 8530 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
e50758c7 8531 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5092de80
GB
8532 gtk_widget_show ( item );
8533#endif
bddd2056 8534
d6de71f9
RN
8535 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8536 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
28c82d8b
EB
8537 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8538 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8539 gtk_widget_show ( item );
8540
6bb72350
RN
8541 /* ATM This function is only available via the layers panel, due to needing a vlp */
8542 if ( vlp ) {
8543 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
0d2b891f
RN
8544 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8545 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6bb72350 8546 if ( item ) {
0d2b891f
RN
8547 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8548 gtk_widget_show ( item );
6bb72350
RN
8549 }
8550 }
c95d6b00 8551
b3eb3b98 8552#ifdef VIK_CONFIG_GEOTAG
0d2b891f
RN
8553 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8554 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8555 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8556 gtk_widget_show ( item );
b3eb3b98 8557#endif
0d2b891f 8558 }
b3eb3b98 8559
0d2b891f 8560 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
c95d6b00
RN
8561 // Only show on viewport popmenu when a trackpoint is selected
8562 if ( ! vlp && l->current_tpl ) {
8563 // Add separator
8564 item = gtk_menu_item_new ();
8565 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8566 gtk_widget_show ( item );
8567
8568 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8569 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8570 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8571 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8572 gtk_widget_show ( item );
8573 }
50a14534
EB
8574 }
8575
667fda15
RN
8576 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8577 GtkWidget *transform_submenu;
8578 transform_submenu = gtk_menu_new ();
8579 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8580 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8581 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8582 gtk_widget_show ( item );
8583 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8584
8585 GtkWidget *dem_submenu;
8586 dem_submenu = gtk_menu_new ();
8587 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8588 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
8589 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8590 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8591
8592 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8594 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8595 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8596 gtk_widget_show ( item );
8597
8598 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8600 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8601 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8602 gtk_widget_show ( item );
8603 }
8604
4d333042
RN
8605 gtk_widget_show_all ( GTK_WIDGET(menu) );
8606
50a14534
EB
8607 return rv;
8608}
8609
491794be
RN
8610// TODO: Probably better to rework this track manipulation in viktrack.c
8611static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
db79f75f 8612{
491794be 8613 // sanity check
db79f75f
RN
8614 if (!vtl->current_tpl)
8615 return;
db79f75f
RN
8616
8617 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
491794be
RN
8618 VikTrackpoint *tp_other = NULL;
8619
8620 if ( before ) {
8621 if (!vtl->current_tpl->prev)
8622 return;
8623 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8624 } else {
8625 if (!vtl->current_tpl->next)
8626 return;
8627 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8628 }
db79f75f 8629
491794be
RN
8630 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8631 if ( tp_other ) {
db79f75f
RN
8632
8633 VikTrackpoint *tp_new = vik_trackpoint_new();
491794be 8634 struct LatLon ll_current, ll_other;
db79f75f 8635 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
491794be 8636 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
db79f75f
RN
8637
8638 /* main positional interpolation */
491794be 8639 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
db79f75f
RN
8640 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8641
8642 /* Now other properties that can be interpolated */
491794be 8643 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
db79f75f 8644
491794be 8645 if (tp_current->has_timestamp && tp_other->has_timestamp) {
db79f75f
RN
8646 /* Note here the division is applied to each part, then added
8647 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 8648 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
db79f75f
RN
8649 tp_new->has_timestamp = TRUE;
8650 }
8651
491794be
RN
8652 if (tp_current->speed != NAN && tp_other->speed != NAN)
8653 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
db79f75f
RN
8654
8655 /* TODO - improve interpolation of course, as it may not be correct.
8656 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8657 [similar applies if value is in radians] */
491794be
RN
8658 if (tp_current->course != NAN && tp_other->course != NAN)
8659 tp_new->course = (tp_current->course + tp_other->course)/2;
db79f75f
RN
8660
8661 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8662
491794be 8663 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
0d2b891f
RN
8664 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8665 if ( !trk )
8666 // Otherwise try routes
8667 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8668 if ( !trk )
8669 return;
8670
8671 gint index = g_list_index ( trk->trackpoints, tp_current );
db79f75f 8672 if ( index > -1 ) {
491794be
RN
8673 if ( !before )
8674 index = index + 1;
20981fd6 8675 // NB no recalculation of bounds since it is inserted between points
491794be 8676 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
db79f75f
RN
8677 }
8678 }
8679}
50a14534 8680
50a14534
EB
8681static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8682{
8683 if ( vtl->tpwin )
8684 {
8685 if ( destroy)
8686 {
8687 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8688 vtl->tpwin = NULL;
8689 }
8690 else
8691 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8692 }
8693 if ( vtl->current_tpl )
8694 {
8695 vtl->current_tpl = NULL;
ce4bd1cf
RN
8696 vtl->current_tp_track = NULL;
8697 vtl->current_tp_id = NULL;
da121f9b 8698 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8699 }
8700}
8701
8ada46de
RN
8702static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8703{
8704 VikTrack *trk = vtl->current_tp_track;
8705 VikCoord vc;
8706 // Notional center of a track is simply an average of the bounding box extremities
8707 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8708 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, &center );
8709 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8710}
8711
50a14534
EB
8712static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8713{
8714 g_assert ( vtl->tpwin != NULL );
8715 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8716 trw_layer_cancel_current_tp ( vtl, TRUE );
0d601fd4
RN
8717
8718 if ( vtl->current_tpl == NULL )
8719 return;
8720
8721 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
50a14534 8722 {
0d2b891f 8723 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8ada46de 8724 my_tpwin_set_tp ( vtl );
50a14534
EB
8725 }
8726 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8727 {
ce4bd1cf 8728 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
0d2b891f
RN
8729 if ( tr == NULL )
8730 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
ce4bd1cf
RN
8731 if ( tr == NULL )
8732 return;
50a14534 8733
a56baa08 8734 trw_layer_trackpoint_selected_delete ( vtl, tr );
ab4553c3 8735
a56baa08 8736 if ( vtl->current_tpl )
ab4553c3 8737 // Reset dialog with the available adjacent trackpoint
8ada46de 8738 my_tpwin_set_tp ( vtl );
50a14534 8739
a56baa08 8740 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8741 }
8742 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8743 {
8ada46de
RN
8744 if ( vtl->current_tp_track ) {
8745 vtl->current_tpl = vtl->current_tpl->next;
8746 my_tpwin_set_tp ( vtl );
8747 }
da121f9b 8748 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
50a14534
EB
8749 }
8750 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8751 {
8ada46de
RN
8752 if ( vtl->current_tp_track ) {
8753 vtl->current_tpl = vtl->current_tpl->prev;
8754 my_tpwin_set_tp ( vtl );
8755 }
da121f9b 8756 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534 8757 }
2880a1de
RN
8758 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8759 {
491794be 8760 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
da121f9b 8761 vik_layer_emit_update(VIK_LAYER(vtl));
2880a1de 8762 }
50a14534 8763 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
da121f9b 8764 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8765}
8766
d6175f49
RN
8767/**
8768 * trw_layer_dialog_shift:
8769 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8770 *
8771 * Try to reposition a dialog if it's over the specified coord
8772 * so to not obscure the item of interest
8773 */
8774void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8775{
8776 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8777
06ce5b5a
RN
8778 // Attempt force dialog to be shown so we can find out where it is more reliably...
8779 while ( gtk_events_pending() )
8780 gtk_main_iteration ();
8781
d6175f49
RN
8782 // get parent window position & size
8783 gint win_pos_x, win_pos_y;
8784 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8785
8786 gint win_size_x, win_size_y;
8787 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8788
8789 // get own dialog size
8790 gint dia_size_x, dia_size_y;
8791 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8792
8793 // get own dialog position
8794 gint dia_pos_x, dia_pos_y;
8795 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8796
8797 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8798 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8799
8800 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8801
8802 gint vp_xx, vp_yy; // In viewport pixels
8803 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8804
8805 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8806
8807 gint dest_x = 0;
8808 gint dest_y = 0;
8809 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8810
8811 // Transform Viewport pixels into absolute pixels
8812 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8813 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8814
8815 // Is dialog over the point (to within an ^^ edge value)
8816 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8817 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8818
8819 if ( vertical ) {
8820 // Shift up<->down
8821 gint hh = vik_viewport_get_height ( vvp );
8822
8823 // Consider the difference in viewport to the full window
8824 gint offset_y = dest_y;
8825 // Add difference between dialog and window sizes
8826 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8827
8828 if ( vp_yy > hh/2 ) {
8829 // Point in bottom half, move window to top half
8830 gtk_window_move ( dialog, dia_pos_x, offset_y );
8831 }
8832 else {
8833 // Point in top half, move dialog down
8834 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8835 }
8836 }
8837 else {
8838 // Shift left<->right
8839 gint ww = vik_viewport_get_width ( vvp );
8840
8841 // Consider the difference in viewport to the full window
8842 gint offset_x = dest_x;
8843 // Add difference between dialog and window sizes
8844 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8845
8846 if ( vp_xx > ww/2 ) {
8847 // Point on right, move window to left
8848 gtk_window_move ( dialog, offset_x, dia_pos_y );
8849 }
8850 else {
8851 // Point on left, move right
8852 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8853 }
8854 }
8855 }
8856 }
8857 }
8858}
8859
50a14534
EB
8860static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8861{
8862 if ( ! vtl->tpwin )
8863 {
8864 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8865 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8866 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8867 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
d6175f49 8868
50a14534 8869 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
d6175f49
RN
8870
8871 if ( vtl->current_tpl ) {
8872 // get tp pixel position
8873 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8874
8875 // Shift up<->down to try not to obscure the trackpoint.
8876 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8877 }
50a14534 8878 }
d6175f49 8879
50a14534 8880 if ( vtl->current_tpl )
ce4bd1cf 8881 if ( vtl->current_tp_track )
8ada46de 8882 my_tpwin_set_tp ( vtl );
50a14534
EB
8883 /* set layer name and TP data */
8884}
8885
941aa6e9
AF
8886/***************************************************************************
8887 ** Tool code
8888 ***************************************************************************/
50a14534 8889
941aa6e9 8890/*** Utility data structures and functions ****/
50a14534
EB
8891
8892typedef struct {
8893 gint x, y;
8894 gint closest_x, closest_y;
7d961c6c 8895 gboolean draw_images;
c9570f86 8896 gpointer *closest_wp_id;
50a14534
EB
8897 VikWaypoint *closest_wp;
8898 VikViewport *vvp;
8899} WPSearchParams;
8900
941aa6e9
AF
8901typedef struct {
8902 gint x, y;
8903 gint closest_x, closest_y;
ce4bd1cf 8904 gpointer closest_track_id;
941aa6e9
AF
8905 VikTrackpoint *closest_tp;
8906 VikViewport *vvp;
8907 GList *closest_tpl;
79773236 8908 LatLonBBox bbox;
941aa6e9
AF
8909} TPSearchParams;
8910
c9570f86 8911static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
50a14534
EB
8912{
8913 gint x, y;
8914 if ( !wp->visible )
8915 return;
8916
8917 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
8918
8919 // If waypoint has an image then use the image size to select
7d961c6c 8920 if ( params->draw_images && wp->image ) {
59b0a97a
RN
8921 gint slackx, slacky;
8922 slackx = wp->image_width / 2;
8923 slacky = wp->image_height / 2;
8924
8925 if ( x <= params->x + slackx && x >= params->x - slackx
8926 && y <= params->y + slacky && y >= params->y - slacky ) {
c9570f86 8927 params->closest_wp_id = id;
59b0a97a
RN
8928 params->closest_wp = wp;
8929 params->closest_x = x;
8930 params->closest_y = y;
8931 }
50a14534 8932 }
59b0a97a
RN
8933 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8934 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8935 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8936 {
c9570f86 8937 params->closest_wp_id = id;
59b0a97a
RN
8938 params->closest_wp = wp;
8939 params->closest_x = x;
8940 params->closest_y = y;
8941 }
50a14534
EB
8942}
8943
ce4bd1cf 8944static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
50a14534 8945{
941aa6e9
AF
8946 GList *tpl = t->trackpoints;
8947 VikTrackpoint *tp;
50a14534 8948
941aa6e9
AF
8949 if ( !t->visible )
8950 return;
50a14534 8951
79773236
RN
8952 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8953 return;
8954
941aa6e9
AF
8955 while (tpl)
8956 {
8957 gint x, y;
8958 tp = VIK_TRACKPOINT(tpl->data);
8959
8960 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8961
8962 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8963 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8964 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 8965 {
ce4bd1cf 8966 params->closest_track_id = id;
941aa6e9
AF
8967 params->closest_tp = tp;
8968 params->closest_tpl = tpl;
8969 params->closest_x = x;
8970 params->closest_y = y;
50a14534 8971 }
941aa6e9 8972 tpl = tpl->next;
50a14534 8973 }
941aa6e9
AF
8974}
8975
0d2b891f
RN
8976// ATM: Leave this as 'Track' only.
8977// Not overly bothered about having a snap to route trackpoint capability
941aa6e9
AF
8978static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8979{
8980 TPSearchParams params;
8981 params.x = x;
8982 params.y = y;
8983 params.vvp = vvp;
ce4bd1cf 8984 params.closest_track_id = NULL;
941aa6e9 8985 params.closest_tp = NULL;
79773236 8986 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9
AF
8987 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8988 return params.closest_tp;
50a14534
EB
8989}
8990
8991static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8992{
8993 WPSearchParams params;
8994 params.x = x;
8995 params.y = y;
8996 params.vvp = vvp;
7d961c6c 8997 params.draw_images = vtl->drawimages;
50a14534 8998 params.closest_wp = NULL;
c9570f86 8999 params.closest_wp_id = NULL;
50a14534
EB
9000 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
9001 return params.closest_wp;
9002}
9003
95d1b757 9004
08f14055
RN
9005// Some forward declarations
9006static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
9007static void marker_moveto ( tool_ed_t *t, gint x, gint y );
9008static void marker_end_move ( tool_ed_t *t );
9009//
9010
c29a3198 9011static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
08f14055
RN
9012{
9013 if ( t->holding ) {
9014 VikCoord new_coord;
9015 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9016
9017 // Here always allow snapping back to the original location
9018 // this is useful when one decides not to move the thing afterall
9019 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
9020
9021 // snap to TP
9022 if ( event->state & GDK_CONTROL_MASK )
9023 {
9024 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9025 if ( tp )
9026 new_coord = tp->coord;
9027 }
9028
9029 // snap to WP
9030 if ( event->state & GDK_SHIFT_MASK )
9031 {
9032 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9033 if ( wp )
9034 new_coord = wp->coord;
9035 }
9036
9037 gint x, y;
9038 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9039
9040 marker_moveto ( t, x, y );
9041
9042 return TRUE;
9043 }
9044 return FALSE;
9045}
9046
9047static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
9048{
9049 if ( t->holding && event->button == 1 )
9050 {
c29a3198
RN
9051 // Prevent accidental (small) shifts when specific movement has not been requested
9052 // (as the click release has occurred within the click object detection area)
9053 if ( !t->moving )
9054 return FALSE;
9055
08f14055
RN
9056 VikCoord new_coord;
9057 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9058
9059 // snap to TP
9060 if ( event->state & GDK_CONTROL_MASK )
9061 {
9062 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9063 if ( tp )
9064 new_coord = tp->coord;
9065 }
9066
9067 // snap to WP
9068 if ( event->state & GDK_SHIFT_MASK )
9069 {
9070 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9071 if ( wp )
9072 new_coord = wp->coord;
9073 }
9074
9075 marker_end_move ( t );
9076
9077 // Determine if working on a waypoint or a trackpoint
aa0665e5 9078 if ( t->is_waypoint ) {
bd78ae7d 9079 // Update waypoint position
08f14055 9080 vtl->current_wp->coord = new_coord;
aa0665e5 9081 trw_layer_calculate_bounds_waypoints ( vtl );
bd78ae7d
RN
9082 // Reset waypoint pointer
9083 vtl->current_wp = NULL;
9084 vtl->current_wp_id = NULL;
aa0665e5 9085 }
08f14055
RN
9086 else {
9087 if ( vtl->current_tpl ) {
ce4bd1cf 9088 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
9089
9090 if ( vtl->current_tp_track )
9091 vik_track_calculate_bounds ( vtl->current_tp_track );
9092
bd78ae7d 9093 if ( vtl->tpwin )
ce4bd1cf 9094 if ( vtl->current_tp_track )
8ada46de 9095 my_tpwin_set_tp ( vtl );
bd78ae7d 9096 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
08f14055
RN
9097 }
9098 }
9099
da121f9b 9100 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055
RN
9101 return TRUE;
9102 }
9103 return FALSE;
9104}
9105
77ad64fa
RN
9106/*
9107 Returns true if a waypoint or track is found near the requested event position for this particular layer
9108 The item found is automatically selected
9109 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
9110 */
08f14055 9111static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
9112{
9113 if ( event->button != 1 )
9114 return FALSE;
9115
9116 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9117 return FALSE;
9118
0d2b891f 9119 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
77ad64fa
RN
9120 return FALSE;
9121
79773236
RN
9122 LatLonBBox bbox;
9123 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
9124
08f14055 9125 // 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 9126
79773236 9127 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
77ad64fa
RN
9128 WPSearchParams wp_params;
9129 wp_params.vvp = vvp;
9130 wp_params.x = event->x;
9131 wp_params.y = event->y;
7d961c6c 9132 wp_params.draw_images = vtl->drawimages;
c9570f86 9133 wp_params.closest_wp_id = NULL;
77ad64fa
RN
9134 wp_params.closest_wp = NULL;
9135
9136 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
9137
9138 if ( wp_params.closest_wp ) {
08f14055
RN
9139
9140 // Select
c9570f86 9141 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
08f14055
RN
9142
9143 // Too easy to move it so must be holding shift to start immediately moving it
3c73e6c4 9144 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
08f14055 9145 if ( event->state & GDK_SHIFT_MASK ||
3c73e6c4 9146 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
08f14055
RN
9147 // Put into 'move buffer'
9148 // NB vvp & vw already set in tet
9149 tet->vtl = (gpointer)vtl;
9150 tet->is_waypoint = TRUE;
9151
9152 marker_begin_move (tet, event->x, event->y);
9153 }
9154
c9570f86
RN
9155 vtl->current_wp = wp_params.closest_wp;
9156 vtl->current_wp_id = wp_params.closest_wp_id;
08f14055 9157
89af1ff6
RN
9158 if ( event->type == GDK_2BUTTON_PRESS ) {
9159 if ( vtl->current_wp->image ) {
9160 menu_array_sublayer values;
9161 values[MA_VTL] = vtl;
9162 values[MA_MISC] = vtl->current_wp->image;
9163 trw_layer_show_picture ( values );
9164 }
9165 }
9166
da121f9b 9167 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055 9168
77ad64fa
RN
9169 return TRUE;
9170 }
9171 }
9172
0d2b891f
RN
9173 // Used for both track and route lists
9174 TPSearchParams tp_params;
9175 tp_params.vvp = vvp;
9176 tp_params.x = event->x;
9177 tp_params.y = event->y;
9178 tp_params.closest_track_id = NULL;
9179 tp_params.closest_tp = NULL;
9414408e 9180 tp_params.closest_tpl = NULL;
79773236 9181 tp_params.bbox = bbox;
77ad64fa 9182
0d2b891f 9183 if (vtl->tracks_visible) {
77ad64fa
RN
9184 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9185
9186 if ( tp_params.closest_tp ) {
08f14055
RN
9187
9188 // Always select + highlight the track
ce4bd1cf 9189 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
08f14055
RN
9190
9191 tet->is_waypoint = FALSE;
9192
9193 // Select the Trackpoint
9194 // Can move it immediately when control held or it's the previously selected tp
9195 if ( event->state & GDK_CONTROL_MASK ||
9196 vtl->current_tpl == tp_params.closest_tpl ) {
9197 // Put into 'move buffer'
9198 // NB vvp & vw already set in tet
9199 tet->vtl = (gpointer)vtl;
9200 marker_begin_move (tet, event->x, event->y);
9201 }
9202
9203 vtl->current_tpl = tp_params.closest_tpl;
ce4bd1cf
RN
9204 vtl->current_tp_id = tp_params.closest_track_id;
9205 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
08f14055 9206
95d1b757
RN
9207 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9208
08f14055 9209 if ( vtl->tpwin )
8ada46de 9210 my_tpwin_set_tp ( vtl );
08f14055 9211
da121f9b 9212 vik_layer_emit_update ( VIK_LAYER(vtl) );
77ad64fa
RN
9213 return TRUE;
9214 }
9215 }
9216
0d2b891f
RN
9217 // Try again for routes
9218 if (vtl->routes_visible) {
9219 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9220
9221 if ( tp_params.closest_tp ) {
9222
9223 // Always select + highlight the track
9224 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9225
9226 tet->is_waypoint = FALSE;
9227
9228 // Select the Trackpoint
9229 // Can move it immediately when control held or it's the previously selected tp
9230 if ( event->state & GDK_CONTROL_MASK ||
9231 vtl->current_tpl == tp_params.closest_tpl ) {
9232 // Put into 'move buffer'
9233 // NB vvp & vw already set in tet
9234 tet->vtl = (gpointer)vtl;
9235 marker_begin_move (tet, event->x, event->y);
9236 }
9237
9238 vtl->current_tpl = tp_params.closest_tpl;
9239 vtl->current_tp_id = tp_params.closest_track_id;
9240 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9241
9242 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9243
9244 if ( vtl->tpwin )
8ada46de 9245 my_tpwin_set_tp ( vtl );
0d2b891f 9246
da121f9b 9247 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
9248 return TRUE;
9249 }
9250 }
9251
77ad64fa 9252 /* these aren't the droids you're looking for */
c9570f86
RN
9253 vtl->current_wp = NULL;
9254 vtl->current_wp_id = NULL;
08f14055
RN
9255 trw_layer_cancel_current_tp ( vtl, FALSE );
9256
95d1b757
RN
9257 // Blank info
9258 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9259
77ad64fa
RN
9260 return FALSE;
9261}
9262
e46f259a
RN
9263static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9264{
9265 if ( event->button != 3 )
9266 return FALSE;
9267
9268 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9269 return FALSE;
9270
0d2b891f 9271 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
e46f259a
RN
9272 return FALSE;
9273
9274 /* Post menu for the currently selected item */
9275
9276 /* See if a track is selected */
9277 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9278 if ( track && track->visible ) {
9279
60a69560 9280 if ( track->name ) {
e46f259a
RN
9281
9282 if ( vtl->track_right_click_menu )
1a7a0378 9283 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
e46f259a
RN
9284
9285 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9286
60a69560
RN
9287 trku_udata udataU;
9288 udataU.trk = track;
9289 udataU.uuid = NULL;
9290
0d2b891f
RN
9291 gpointer *trkf;
9292 if ( track->is_route )
9293 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9294 else
9295 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
60a69560
RN
9296
9297 if ( trkf && udataU.uuid ) {
9298
0d2b891f
RN
9299 GtkTreeIter *iter;
9300 if ( track->is_route )
9301 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9302 else
9303 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
60a69560
RN
9304
9305 trw_layer_sublayer_add_menu_items ( vtl,
9306 vtl->track_right_click_menu,
9307 NULL,
0d2b891f 9308 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
60a69560
RN
9309 udataU.uuid,
9310 iter,
9311 vvp );
9312 }
e46f259a
RN
9313
9314 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9315
9316 return TRUE;
9317 }
9318 }
9319
9320 /* See if a waypoint is selected */
9321 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9322 if ( waypoint && waypoint->visible ) {
60a69560 9323 if ( waypoint->name ) {
e46f259a
RN
9324
9325 if ( vtl->wp_right_click_menu )
1a7a0378 9326 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
e46f259a
RN
9327
9328 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
60a69560
RN
9329
9330 wpu_udata udata;
9331 udata.wp = waypoint;
9332 udata.uuid = NULL;
9333
9334 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9335
9336 if ( wpf && udata.uuid ) {
9337 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9338
9339 trw_layer_sublayer_add_menu_items ( vtl,
9340 vtl->wp_right_click_menu,
9341 NULL,
9342 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9343 udata.uuid,
9344 iter,
9345 vvp );
9346 }
e46f259a
RN
9347 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9348
9349 return TRUE;
9350 }
9351 }
9352
9353 return FALSE;
9354}
9355
7432fddf
AF
9356/* background drawing hook, to be passed the viewport */
9357static gboolean tool_sync_done = TRUE;
9358
9359static gboolean tool_sync(gpointer data)
9360{
9361 VikViewport *vvp = data;
9362 gdk_threads_enter();
9363 vik_viewport_sync(vvp);
9364 tool_sync_done = TRUE;
9365 gdk_threads_leave();
9366 return FALSE;
9367}
9368
7432fddf
AF
9369static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9370{
9371 t->holding = TRUE;
9372 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9373 gdk_gc_set_function ( t->gc, GDK_INVERT );
9374 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9375 vik_viewport_sync(t->vvp);
9376 t->oldx = x;
9377 t->oldy = y;
c29a3198 9378 t->moving = FALSE;
7432fddf
AF
9379}
9380
9381static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9382{
9383 VikViewport *vvp = t->vvp;
9384 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9385 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9386 t->oldx = x;
9387 t->oldy = y;
c29a3198 9388 t->moving = TRUE;
7b203521 9389
7432fddf
AF
9390 if (tool_sync_done) {
9391 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9392 tool_sync_done = FALSE;
9393 }
9394}
9395
9396static void marker_end_move ( tool_ed_t *t )
9397{
9398 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9399 g_object_unref ( t->gc );
9400 t->holding = FALSE;
c29a3198 9401 t->moving = FALSE;
7432fddf
AF
9402}
9403
941aa6e9
AF
9404/*** Edit waypoint ****/
9405
9406static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9407{
7432fddf
AF
9408 tool_ed_t *t = g_new(tool_ed_t, 1);
9409 t->vvp = vvp;
9410 t->holding = FALSE;
9411 return t;
941aa6e9
AF
9412}
9413
919ed63e
RN
9414static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9415{
9416 g_free ( t );
9417}
9418
7432fddf 9419static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
9420{
9421 WPSearchParams params;
7432fddf
AF
9422 tool_ed_t *t = data;
9423 VikViewport *vvp = t->vvp;
50a14534 9424
941aa6e9
AF
9425 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9426 return FALSE;
7432fddf
AF
9427
9428 if ( t->holding ) {
9429 return TRUE;
9430 }
9431
87741170
RN
9432 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9433 return FALSE;
9434
50a14534
EB
9435 if ( vtl->current_wp && vtl->current_wp->visible )
9436 {
9437 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9438 gint x, y;
9439 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9440
c75d78d7
AF
9441 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9442 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
9443 {
9444 if ( event->button == 3 )
9445 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
9446 else {
9447 marker_begin_move(t, event->x, event->y);
9448 }
50a14534
EB
9449 return TRUE;
9450 }
9451 }
9452
9453 params.vvp = vvp;
9454 params.x = event->x;
9455 params.y = event->y;
7d961c6c 9456 params.draw_images = vtl->drawimages;
c9570f86 9457 params.closest_wp_id = NULL;
50a14534
EB
9458 params.closest_wp = NULL;
9459 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
98356138 9460 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
50a14534 9461 {
a129aa51
RN
9462 if ( event->button == 3 )
9463 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9464 else
9465 marker_begin_move(t, event->x, event->y);
d84ade77 9466 return FALSE;
50a14534
EB
9467 }
9468 else if ( params.closest_wp )
9469 {
9470 if ( event->button == 3 )
9471 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9472 else
9473 vtl->waypoint_rightclick = FALSE;
9474
c9570f86 9475 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8623d370 9476
50a14534 9477 vtl->current_wp = params.closest_wp;
c9570f86 9478 vtl->current_wp_id = params.closest_wp_id;
50a14534 9479
50a14534 9480 /* could make it so don't update if old WP is off screen and new is null but oh well */
da121f9b 9481 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9482 return TRUE;
9483 }
9484
9485 vtl->current_wp = NULL;
c9570f86 9486 vtl->current_wp_id = NULL;
50a14534 9487 vtl->waypoint_rightclick = FALSE;
da121f9b 9488 vik_layer_emit_update ( VIK_LAYER(vtl) );
7432fddf
AF
9489 return FALSE;
9490}
9491
dc2c040e 9492static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
9493{
9494 tool_ed_t *t = data;
9495 VikViewport *vvp = t->vvp;
9496
9497 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9498 return FALSE;
9499
9500 if ( t->holding ) {
9501 VikCoord new_coord;
9502 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9503
9504 /* snap to TP */
9505 if ( event->state & GDK_CONTROL_MASK )
9506 {
9507 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9508 if ( tp )
9509 new_coord = tp->coord;
9510 }
9511
9512 /* snap to WP */
9513 if ( event->state & GDK_SHIFT_MASK )
9514 {
9515 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9516 if ( wp && wp != vtl->current_wp )
9517 new_coord = wp->coord;
9518 }
9519
9520 {
9521 gint x, y;
9522 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 9523
7432fddf
AF
9524 marker_moveto ( t, x, y );
9525 }
9526 return TRUE;
9527 }
50a14534
EB
9528 return FALSE;
9529}
9530
7432fddf 9531static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9532{
7432fddf
AF
9533 tool_ed_t *t = data;
9534 VikViewport *vvp = t->vvp;
9535
941aa6e9
AF
9536 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9537 return FALSE;
7432fddf
AF
9538
9539 if ( t->holding && event->button == 1 )
941aa6e9
AF
9540 {
9541 VikCoord new_coord;
941aa6e9
AF
9542 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9543
9544 /* snap to TP */
9545 if ( event->state & GDK_CONTROL_MASK )
9546 {
9547 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9548 if ( tp )
9549 new_coord = tp->coord;
9550 }
9551
9552 /* snap to WP */
9553 if ( event->state & GDK_SHIFT_MASK )
9554 {
9555 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9556 if ( wp && wp != vtl->current_wp )
9557 new_coord = wp->coord;
9558 }
9559
7432fddf
AF
9560 marker_end_move ( t );
9561
941aa6e9 9562 vtl->current_wp->coord = new_coord;
aa0665e5
RN
9563
9564 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9565 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9566 return TRUE;
9567 }
9568 /* PUT IN RIGHT PLACE!!! */
7432fddf 9569 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
9570 {
9571 if ( vtl->wp_right_click_menu )
4f14a010 9572 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
0b951ebe
RN
9573 if ( vtl->current_wp ) {
9574 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
c9570f86 9575 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
9576 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9577 }
941aa6e9
AF
9578 vtl->waypoint_rightclick = FALSE;
9579 }
9580 return FALSE;
9581}
9582
9583/*** New track ****/
9584
9585static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9586{
9587 return vvp;
9588}
9589
7b203521
EB
9590typedef struct {
9591 VikTrwLayer *vtl;
745fda83
RN
9592 GdkDrawable *drawable;
9593 GdkGC *gc;
9594 GdkPixmap *pixmap;
9595} draw_sync_t;
7b203521 9596
745fda83
RN
9597/*
9598 * Draw specified pixmap
9599 */
9600static gboolean draw_sync ( gpointer data )
7b203521 9601{
745fda83 9602 draw_sync_t *ds = (draw_sync_t*) data;
ef5e8132
RN
9603 // Sometimes don't want to draw
9604 // normally because another update has taken precedent such as panning the display
9605 // which means this pixmap is no longer valid
9606 if ( ds->vtl->draw_sync_do ) {
9607 gdk_threads_enter();
9608 gdk_draw_drawable (ds->drawable,
9609 ds->gc,
9610 ds->pixmap,
9611 0, 0, 0, 0, -1, -1);
9612 ds->vtl->draw_sync_done = TRUE;
9613 gdk_threads_leave();
9614 }
351eb253 9615 g_free ( ds );
7b203521
EB
9616 return FALSE;
9617}
9618
19a0d58f 9619static gchar* distance_string (gdouble distance)
8da84040
RN
9620{
9621 gchar str[128];
9622
9623 /* draw label with distance */
9624 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9625 switch (dist_units) {
9626 case VIK_UNITS_DISTANCE_MILES:
9627 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9628 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9629 } else if (distance < 1609.4) {
9630 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9631 } else {
9632 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9633 }
9634 break;
b22233bd
RN
9635 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9636 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9637 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9638 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9639 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9640 } else {
9641 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9642 }
9643 break;
8da84040
RN
9644 default:
9645 // VIK_UNITS_DISTANCE_KILOMETRES
9646 if (distance >= 1000 && distance < 100000) {
9647 g_sprintf(str, "%3.2f km", distance/1000.0);
9648 } else if (distance < 1000) {
9649 g_sprintf(str, "%d m", (int)distance);
9650 } else {
9651 g_sprintf(str, "%d km", (int)distance/1000);
9652 }
9653 break;
9654 }
9655 return g_strdup (str);
9656}
9657
9658/*
9659 * Actually set the message in statusbar
9660 */
9a3538f5 9661static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8da84040
RN
9662{
9663 // Only show elevation data when track has some elevation properties
9664 gchar str_gain_loss[64];
9665 str_gain_loss[0] = '\0';
9a3538f5
GB
9666 gchar str_last_step[64];
9667 str_last_step[0] = '\0';
9668 gchar *str_total = distance_string (distance);
8da84040
RN
9669
9670 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9671 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9672 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9673 else
9674 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9675 }
9a3538f5
GB
9676
9677 if ( last_step > 0 ) {
9678 gchar *tmp = distance_string (last_step);
0da53bd9 9679 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9a3538f5
GB
9680 g_free ( tmp );
9681 }
9682
9683 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8da84040
RN
9684
9685 // Write with full gain/loss information
9a3538f5
GB
9686 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9687 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8da84040 9688 g_free ( msg );
9a3538f5 9689 g_free ( str_total );
8da84040
RN
9690}
9691
9692/*
9693 * Figure out what information should be set in the statusbar and then write it
9694 */
9695static void update_statusbar ( VikTrwLayer *vtl )
9696{
9697 // Get elevation data
9698 gdouble elev_gain, elev_loss;
9699 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9700
9701 /* Find out actual distance of current track */
9702 gdouble distance = vik_track_get_length (vtl->current_track);
8da84040 9703
9a3538f5 9704 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8da84040
RN
9705}
9706
9707
dc2c040e 9708static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
9709{
9710 /* if we haven't sync'ed yet, we don't have time to do more. */
745fda83 9711 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
215ebe59 9712 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
745fda83
RN
9713
9714 static GdkPixmap *pixmap = NULL;
9715 int w1, h1, w2, h2;
9716 // Need to check in case window has been resized
9717 w1 = vik_viewport_get_width(vvp);
9718 h1 = vik_viewport_get_height(vvp);
9719 if (!pixmap) {
9b082b39 9720 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9721 }
9722 gdk_drawable_get_size (pixmap, &w2, &h2);
9723 if (w1 != w2 || h1 != h2) {
9724 g_object_unref ( G_OBJECT ( pixmap ) );
9b082b39 9725 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9726 }
9727
9728 // Reset to background
9729 gdk_draw_drawable (pixmap,
9730 vtl->current_track_newpoint_gc,
9731 vik_viewport_get_pixmap(vvp),
9732 0, 0, 0, 0, -1, -1);
9733
9734 draw_sync_t *passalong;
7b203521
EB
9735 gint x1, y1;
9736
c31b3fbb 9737 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
745fda83
RN
9738
9739 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9740 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9741 // thus when we come to reset to the background it would include what we have already drawn!!
9742 gdk_draw_line ( pixmap,
9743 vtl->current_track_newpoint_gc,
9744 x1, y1, event->x, event->y );
9745 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
23ea1329 9746
23ea1329
RN
9747 /* Find out actual distance of current track */
9748 gdouble distance = vik_track_get_length (vtl->current_track);
9749
9750 // Now add distance to where the pointer is //
9751 VikCoord coord;
9752 struct LatLon ll;
9753 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9754 vik_coord_to_latlon ( &coord, &ll );
9a3538f5
GB
9755 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9756 distance = distance + last_step;
23ea1329 9757
8da84040
RN
9758 // Get elevation data
9759 gdouble elev_gain, elev_loss;
9760 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9761
9762 // Adjust elevation data (if available) for the current pointer position
9763 gdouble elev_new;
9764 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9765 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
c31b3fbb 9766 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8da84040 9767 // Adjust elevation of last track point
c31b3fbb 9768 if ( elev_new > last_tpt->altitude )
8da84040 9769 // Going up
c31b3fbb 9770 elev_gain += elev_new - last_tpt->altitude;
8da84040
RN
9771 else
9772 // Going down
c31b3fbb 9773 elev_loss += last_tpt->altitude - elev_new;
23ea1329 9774 }
23ea1329 9775 }
6ba8e356 9776
90611609
RN
9777 //
9778 // Display of the distance 'tooltip' during track creation is controlled by a preference
9779 //
9780 if ( a_vik_get_create_track_tooltip() ) {
9781
9782 gchar *str = distance_string (distance);
061ccfdc 9783
90611609
RN
9784 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9785 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9786 pango_layout_set_text (pl, str, -1);
9787 gint wd, hd;
9788 pango_layout_get_pixel_size ( pl, &wd, &hd );
6ba8e356 9789
90611609
RN
9790 gint xd,yd;
9791 // offset from cursor a bit depending on font size
9792 xd = event->x + 10;
9793 yd = event->y - hd;
56cb1807 9794
90611609
RN
9795 // Create a background block to make the text easier to read over the background map
9796 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9797 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9798 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
745fda83 9799
90611609
RN
9800 g_object_unref ( G_OBJECT ( pl ) );
9801 g_object_unref ( G_OBJECT ( background_block_gc ) );
9802 g_free (str);
9803 }
6ba8e356 9804
745fda83 9805 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7b203521 9806 passalong->vtl = vtl;
745fda83 9807 passalong->pixmap = pixmap;
9b082b39 9808 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
745fda83 9809 passalong->gc = vtl->current_track_newpoint_gc;
8da84040 9810
9a3538f5
GB
9811 gdouble angle;
9812 gdouble baseangle;
9813 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9814
8da84040 9815 // Update statusbar with full gain/loss information
9a3538f5 9816 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7b203521 9817
745fda83
RN
9818 // draw pixmap when we have time to
9819 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9820 vtl->draw_sync_done = FALSE;
165d30aa 9821 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 9822 }
165d30aa 9823 return VIK_LAYER_TOOL_ACK;
7b203521
EB
9824}
9825
215ebe59
RN
9826// NB vtl->current_track must be valid
9827static void undo_trackpoint_add ( VikTrwLayer *vtl )
9828{
9829 // 'undo'
9830 if ( vtl->current_track->trackpoints ) {
9831 // TODO rework this...
9832 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9833 GList *last = g_list_last(vtl->current_track->trackpoints);
9834 g_free ( last->data );
9835 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9836
9837 vik_track_calculate_bounds ( vtl->current_track );
9838 }
9839}
9840
777e2d4d
EB
9841static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9842{
9843 if ( vtl->current_track && event->keyval == GDK_Escape ) {
a000257b
RN
9844 // Bin track if only one point as it's not very useful
9845 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9846 if ( vtl->current_track->is_route )
9847 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9848 else
9849 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9850 }
777e2d4d 9851 vtl->current_track = NULL;
da121f9b 9852 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9853 return TRUE;
9854 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
215ebe59 9855 undo_trackpoint_add ( vtl );
8da84040 9856 update_statusbar ( vtl );
da121f9b 9857 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9858 return TRUE;
9859 }
9860 return FALSE;
9861}
9862
e37b2a6d
RN
9863/*
9864 * Common function to handle trackpoint button requests on either a route or a track
9865 * . enables adding a point via normal click
9866 * . enables removal of last point via right click
9867 * . finishing of the track or route via double clicking
9868 */
9869static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
9870{
9871 VikTrackpoint *tp;
9872
941aa6e9
AF
9873 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9874 return FALSE;
9875
ef5e8132
RN
9876 if ( event->button == 2 ) {
9877 // As the display is panning, the new track pixmap is now invalid so don't draw it
9878 // otherwise this drawing done results in flickering back to an old image
9879 vtl->draw_sync_do = FALSE;
9880 return FALSE;
9881 }
9882
e37b2a6d 9883 if ( event->button == 3 )
50a14534 9884 {
e37b2a6d
RN
9885 if ( !vtl->current_track )
9886 return FALSE;
215ebe59 9887 undo_trackpoint_add ( vtl );
8da84040 9888 update_statusbar ( vtl );
da121f9b 9889 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9890 return TRUE;
9891 }
9892
9893 if ( event->type == GDK_2BUTTON_PRESS )
9894 {
9895 /* subtract last (duplicate from double click) tp then end */
9896 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9897 {
50a14534 9898 /* undo last, then end */
215ebe59 9899 undo_trackpoint_add ( vtl );
50a14534
EB
9900 vtl->current_track = NULL;
9901 }
da121f9b 9902 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9903 return TRUE;
9904 }
9905
50a14534
EB
9906 tp = vik_trackpoint_new();
9907 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9908
9909 /* snap to other TP */
9910 if ( event->state & GDK_CONTROL_MASK )
9911 {
9912 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9913 if ( other_tp )
9914 tp->coord = other_tp->coord;
9915 }
9916
9917 tp->newsegment = FALSE;
9918 tp->has_timestamp = FALSE;
9919 tp->timestamp = 0;
e37b2a6d
RN
9920
9921 if ( vtl->current_track ) {
9bc95d58 9922 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
e37b2a6d
RN
9923 /* Auto attempt to get elevation from DEM data (if it's available) */
9924 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9925 }
50a14534
EB
9926
9927 vtl->ct_x1 = vtl->ct_x2;
9928 vtl->ct_y1 = vtl->ct_y2;
9929 vtl->ct_x2 = event->x;
9930 vtl->ct_y2 = event->y;
9931
da121f9b 9932 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9933 return TRUE;
9934}
9935
e37b2a6d
RN
9936static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9937{
ee43b101
MH
9938 // if we were running the route finder, cancel it
9939 vtl->route_finder_started = FALSE;
9940
e37b2a6d
RN
9941 // ----------------------------------------------------- if current is a route - switch to new track
9942 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9943 {
9944 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4207cba3
RN
9945 if ( a_vik_get_ask_for_create_track_name() ) {
9946 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
9947 if ( !name )
9948 return FALSE;
e37b2a6d 9949 }
4207cba3
RN
9950 new_track_create_common ( vtl, name );
9951 g_free ( name );
e37b2a6d
RN
9952 }
9953 return tool_new_track_or_route_click ( vtl, event, vvp );
9954}
9955
ef5e8132
RN
9956static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9957{
9958 if ( event->button == 2 ) {
9959 // Pan moving ended - enable potential point drawing again
9960 vtl->draw_sync_do = TRUE;
9961 vtl->draw_sync_done = TRUE;
9962 }
9963}
9964
e37b2a6d
RN
9965/*** New route ****/
9966
9967static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9968{
9969 return vvp;
9970}
9971
9972static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9973{
ee43b101
MH
9974 // if we were running the route finder, cancel it
9975 vtl->route_finder_started = FALSE;
9976
9977 // -------------------------- if current is a track - switch to new route,
9978 if ( event->button == 1 && ( ! vtl->current_track ||
9979 (vtl->current_track && !vtl->current_track->is_route ) ) )
e37b2a6d
RN
9980 {
9981 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
4207cba3
RN
9982 if ( a_vik_get_ask_for_create_track_name() ) {
9983 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
9984 if ( !name )
9985 return FALSE;
1613e468 9986 }
4207cba3
RN
9987 new_route_create_common ( vtl, name );
9988 g_free ( name );
e37b2a6d
RN
9989 }
9990 return tool_new_track_or_route_click ( vtl, event, vvp );
9991}
9992
941aa6e9
AF
9993/*** New waypoint ****/
9994
9995static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9996{
9997 return vvp;
9998}
9999
10000static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10001{
10002 VikCoord coord;
10003 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10004 return FALSE;
10005 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
aa0665e5
RN
10006 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
10007 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 10008 vik_layer_emit_update ( VIK_LAYER(vtl) );
aa0665e5 10009 }
941aa6e9
AF
10010 return TRUE;
10011}
10012
10013
10014/*** Edit trackpoint ****/
10015
10016static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
10017{
7432fddf 10018 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
10019 t->vvp = vvp;
10020 t->holding = FALSE;
10021 return t;
941aa6e9
AF
10022}
10023
919ed63e
RN
10024static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
10025{
10026 g_free ( t );
10027}
10028
33534cd8 10029static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 10030{
7432fddf 10031 tool_ed_t *t = data;
33534cd8
AF
10032 VikViewport *vvp = t->vvp;
10033 TPSearchParams params;
941aa6e9
AF
10034 /* OUTDATED DOCUMENTATION:
10035 find 5 pixel range on each side. then put these UTM, and a pointer
10036 to the winning track name (and maybe the winning track itself), and a
10037 pointer to the winning trackpoint, inside an array or struct. pass
10038 this along, do a foreach on the tracks which will do a foreach on the
10039 trackpoints. */
10040 params.vvp = vvp;
10041 params.x = event->x;
10042 params.y = event->y;
ce4bd1cf 10043 params.closest_track_id = NULL;
941aa6e9
AF
10044 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
10045 params.closest_tp = NULL;
9414408e 10046 params.closest_tpl = NULL;
acf5b0d4 10047 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9 10048
7432fddf
AF
10049 if ( event->button != 1 )
10050 return FALSE;
10051
941aa6e9
AF
10052 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10053 return FALSE;
10054
0d2b891f 10055 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
87741170
RN
10056 return FALSE;
10057
941aa6e9
AF
10058 if ( vtl->current_tpl )
10059 {
10060 /* 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.) */
10061 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
ce4bd1cf
RN
10062 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
10063 if ( !current_tr )
10064 return FALSE;
941aa6e9 10065
ce4bd1cf 10066 gint x, y;
941aa6e9
AF
10067 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
10068
10069 if ( current_tr->visible &&
10070 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
10071 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
10072 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
10073 return TRUE;
10074 }
10075
941aa6e9
AF
10076 }
10077
0d2b891f
RN
10078 if ( vtl->tracks_visible )
10079 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
941aa6e9
AF
10080
10081 if ( params.closest_tp )
10082 {
ce4bd1cf 10083 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
941aa6e9 10084 vtl->current_tpl = params.closest_tpl;
ce4bd1cf 10085 vtl->current_tp_id = params.closest_track_id;
da14cc69 10086 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
941aa6e9 10087 trw_layer_tpwin_init ( vtl );
95d1b757 10088 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 10089 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
10090 return TRUE;
10091 }
10092
0d2b891f
RN
10093 if ( vtl->routes_visible )
10094 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
10095
10096 if ( params.closest_tp )
10097 {
10098 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
10099 vtl->current_tpl = params.closest_tpl;
10100 vtl->current_tp_id = params.closest_track_id;
10101 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
10102 trw_layer_tpwin_init ( vtl );
10103 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 10104 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
10105 return TRUE;
10106 }
10107
941aa6e9
AF
10108 /* these aren't the droids you're looking for */
10109 return FALSE;
10110}
10111
dc2c040e 10112static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 10113{
7432fddf 10114 tool_ed_t *t = data;
33534cd8
AF
10115 VikViewport *vvp = t->vvp;
10116
10117 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10118 return FALSE;
10119
10120 if ( t->holding )
10121 {
10122 VikCoord new_coord;
33534cd8
AF
10123 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10124
10125 /* snap to TP */
10126 if ( event->state & GDK_CONTROL_MASK )
10127 {
10128 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10129 if ( tp && tp != vtl->current_tpl->data )
10130 new_coord = tp->coord;
10131 }
10132 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
10133 {
10134 gint x, y;
10135 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
10136 marker_moveto ( t, x, y );
10137 }
33534cd8
AF
10138
10139 return TRUE;
10140 }
10141 return FALSE;
10142}
10143
33534cd8 10144static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 10145{
7432fddf 10146 tool_ed_t *t = data;
33534cd8
AF
10147 VikViewport *vvp = t->vvp;
10148
941aa6e9
AF
10149 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10150 return FALSE;
7432fddf
AF
10151 if ( event->button != 1)
10152 return FALSE;
33534cd8 10153
7432fddf 10154 if ( t->holding ) {
941aa6e9 10155 VikCoord new_coord;
941aa6e9
AF
10156 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10157
10158 /* snap to TP */
10159 if ( event->state & GDK_CONTROL_MASK )
10160 {
10161 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10162 if ( tp && tp != vtl->current_tpl->data )
10163 new_coord = tp->coord;
10164 }
10165
10166 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
10167 if ( vtl->current_tp_track )
10168 vik_track_calculate_bounds ( vtl->current_tp_track );
941aa6e9 10169
7432fddf 10170 marker_end_move ( t );
33534cd8 10171
941aa6e9 10172 /* diff dist is diff from orig */
46b6631a 10173 if ( vtl->tpwin )
8ada46de 10174 my_tpwin_set_tp ( vtl );
941aa6e9 10175
da121f9b 10176 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
10177 return TRUE;
10178 }
10179 return FALSE;
10180}
10181
10182
ee43b101
MH
10183/*** Extended Route Finder ***/
10184
10185static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
1eef1bde
QT
10186{
10187 return vvp;
10188}
10189
ee43b101
MH
10190static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10191{
10192 VikCoord *new_end;
10193 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10194 if ( new_end ) {
10195 g_free ( new_end );
10196 vik_layer_emit_update ( VIK_LAYER(vtl) );
10197
10198 /* remove last ' to:...' */
10199 if ( vtl->current_track->comment ) {
10200 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10201 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10202 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10203 last_to - vtl->current_track->comment - 1);
10204 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10205 }
10206 }
10207 }
10208}
10209
10210
10211static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
1eef1bde
QT
10212{
10213 VikCoord tmp;
0c1044e9 10214 if ( !vtl ) return FALSE;
1eef1bde 10215 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
ee43b101
MH
10216 if ( event->button == 3 && vtl->current_track ) {
10217 tool_extended_route_finder_undo ( vtl );
10218 }
10219 else if ( event->button == 2 ) {
10220 vtl->draw_sync_do = FALSE;
10221 return FALSE;
c3deba01 10222 }
ee43b101
MH
10223 // if we started the track but via undo deleted all the track points, begin again
10224 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10225 return tool_new_track_or_route_click ( vtl, event, vvp );
10226 }
10227 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10228 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
1eef1bde 10229 struct LatLon start, end;
bddd2056 10230
ee43b101
MH
10231 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10232 vik_coord_to_latlon ( &(tp_start->coord), &start );
1eef1bde 10233 vik_coord_to_latlon ( &(tmp), &end );
bddd2056 10234
ee43b101
MH
10235 vtl->route_finder_started = TRUE;
10236 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10237
10238 // update UI to let user know what's going on
10239 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10240 VikRoutingEngine *engine = vik_routing_default_engine ( );
10241 if ( ! engine ) {
10242 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10243 return TRUE;
bddd2056 10244 }
ee43b101
MH
10245 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10246 vik_routing_engine_get_label ( engine ),
10247 start.lat, start.lon, end.lat, end.lon );
10248 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10249 g_free ( msg );
10250 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
bddd2056 10251
bddd2056 10252
ee43b101
MH
10253 /* Give GTK a change to display the new status bar before querying the web */
10254 while ( gtk_events_pending ( ) )
10255 gtk_main_iteration ( );
20981fd6 10256
ee43b101 10257 gboolean find_status = vik_routing_default_find ( vtl, start, end );
20981fd6 10258
ee43b101
MH
10259 /* Update UI to say we're done */
10260 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10261 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10262 vik_routing_engine_get_label ( engine ),
10263 start.lat, start.lon, end.lat, end.lon )
10264 : g_strdup_printf ( _("Error getting route from %s."),
10265 vik_routing_engine_get_label ( engine ) );
10266 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10267 g_free ( msg );
bddd2056 10268
da121f9b 10269 vik_layer_emit_update ( VIK_LAYER(vtl) );
1eef1bde 10270 } else {
ee43b101
MH
10271 vtl->current_track = NULL;
10272
10273 // create a new route where we will add the planned route to
10274 gboolean ret = tool_new_route_click( vtl, event, vvp );
10275
7ff7d728 10276 vtl->route_finder_started = TRUE;
ee43b101
MH
10277
10278 return ret;
1eef1bde 10279 }
1eef1bde
QT
10280 return TRUE;
10281}
10282
ee43b101
MH
10283static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10284{
10285 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10286 vtl->route_finder_started = FALSE;
10287 vtl->current_track = NULL;
10288 vik_layer_emit_update ( VIK_LAYER(vtl) );
10289 return TRUE;
10290 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10291 tool_extended_route_finder_undo ( vtl );
10292 }
10293 return FALSE;
10294}
10295
10296
10297
941aa6e9
AF
10298/*** Show picture ****/
10299
10300static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10301{
10302 return vvp;
10303}
10304
10305/* Params are: vvp, event, last match found or NULL */
9e212bfc 10306static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
941aa6e9
AF
10307{
10308 if ( wp->image && wp->visible )
10309 {
10310 gint x, y, slackx, slacky;
10311 GdkEventButton *event = (GdkEventButton *) params[1];
10312
10313 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10314 slackx = wp->image_width / 2;
10315 slacky = wp->image_height / 2;
10316 if ( x <= event->x + slackx && x >= event->x - slackx
10317 && y <= event->y + slacky && y >= event->y - slacky )
10318 {
10319 params[2] = wp->image; /* we've found a match. however continue searching
10320 * since we want to find the last match -- that
10321 * is, the match that was drawn last. */
10322 }
10323 }
10324}
10325
19782ffd 10326static void trw_layer_show_picture ( menu_array_sublayer values )
a412f3f5
RN
10327{
10328 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10329#ifdef WINDOWS
19782ffd 10330 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
a412f3f5
RN
10331#else /* WINDOWS */
10332 GError *err = NULL;
19782ffd 10333 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
3317dc4e 10334 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
a412f3f5
RN
10335 g_free ( quoted_file );
10336 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10337 {
19782ffd 10338 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
10339 g_error_free ( err );
10340 }
10341 g_free ( cmd );
10342#endif /* WINDOWS */
10343}
10344
941aa6e9
AF
10345static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10346{
10347 gpointer params[3] = { vvp, event, NULL };
10348 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10349 return FALSE;
10350 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10351 if ( params[2] )
10352 {
19782ffd
RN
10353 static menu_array_sublayer values;
10354 values[MA_VTL] = vtl;
10355 values[MA_MISC] = params[2];
10356 trw_layer_show_picture ( values );
941aa6e9
AF
10357 return TRUE; /* found a match */
10358 }
10359 else
10360 return FALSE; /* go through other layers, searching for a match */
10361}
10362
10363/***************************************************************************
10364 ** End tool code
10365 ***************************************************************************/
10366
10367
9e212bfc 10368static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
50a14534
EB
10369{
10370 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10371 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10372}
10373
91822ddd
RN
10374/* Structure for thumbnail creating data used in the background thread */
10375typedef struct {
10376 VikTrwLayer *vtl; // Layer needed for redrawing
10377 GSList *pics; // Image list
10378} thumbnail_create_thread_data;
10379
10380static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 10381{
91822ddd
RN
10382 guint total = g_slist_length(tctd->pics), done = 0;
10383 while ( tctd->pics )
50a14534 10384 {
91822ddd 10385 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
10386 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10387 if ( result != 0 )
10388 return -1; /* Abort thread */
10389
91822ddd 10390 tctd->pics = tctd->pics->next;
50a14534 10391 }
91822ddd
RN
10392
10393 // Redraw to show the thumbnails as they are now created
91822ddd 10394 if ( IS_VIK_LAYER(tctd->vtl) )
da121f9b 10395 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
91822ddd 10396
17c8aefa 10397 return 0;
50a14534
EB
10398}
10399
91822ddd 10400static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 10401{
91822ddd 10402 while ( tctd->pics )
50a14534 10403 {
91822ddd
RN
10404 g_free ( tctd->pics->data );
10405 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 10406 }
91822ddd 10407 g_free ( tctd );
50a14534
EB
10408}
10409
b3eb3b98 10410void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
50a14534
EB
10411{
10412 if ( ! vtl->has_verified_thumbnails )
10413 {
10414 GSList *pics = NULL;
10415 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10416 if ( pics )
10417 {
10418 gint len = g_slist_length ( pics );
4c77d5e0 10419 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
10420 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10421 tctd->vtl = vtl;
10422 tctd->pics = pics;
c75da936
RN
10423 a_background_thread ( BACKGROUND_POOL_LOCAL,
10424 VIK_GTK_WINDOW_FROM_LAYER(vtl),
91822ddd
RN
10425 tmp,
10426 (vik_thr_func) create_thumbnails_thread,
10427 tctd,
10428 (vik_thr_free_func) thumbnail_create_thread_free,
10429 NULL,
10430 len );
50a14534
EB
10431 g_free ( tmp );
10432 }
10433 }
10434}
10435
b1453c16
RN
10436static const gchar* my_track_colors ( gint ii )
10437{
10438 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10439 "#2d870a",
ca738b73 10440 "#135D34",
b1453c16
RN
10441 "#0a8783",
10442 "#0e4d87",
10443 "#05469f",
ca738b73 10444 "#695CBB",
b1453c16
RN
10445 "#2d059f",
10446 "#4a059f",
ca738b73 10447 "#5A171A",
b1453c16
RN
10448 "#96059f"
10449 };
10450 // Fast and reliable way of returning a colour
10451 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10452}
10453
10454static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10455{
10456 GHashTableIter iter;
10457 gpointer key, value;
10458
10459 gint ii = 0;
10460 // Tracks
10461 g_hash_table_iter_init ( &iter, vtl->tracks );
10462
10463 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10464
10465 // Tracks get a random spread of colours if not already assigned
10466 if ( ! VIK_TRACK(value)->has_color ) {
e2bc000f
RN
10467 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10468 VIK_TRACK(value)->color = vtl->track_color;
10469 else {
10470 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10471 }
b1453c16
RN
10472 VIK_TRACK(value)->has_color = TRUE;
10473 }
10474
93ee73b3 10475 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10476
10477 ii++;
10478 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10479 ii = 0;
10480 }
10481
10482 // Routes
10483 ii = 0;
10484 g_hash_table_iter_init ( &iter, vtl->routes );
10485
10486 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10487
10488 // Routes get an intermix of reds
10489 if ( ! VIK_TRACK(value)->has_color ) {
10490 if ( ii )
10491 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10492 else
10493 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10494 VIK_TRACK(value)->has_color = TRUE;
10495 }
10496
93ee73b3 10497 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10498
10499 ii = !ii;
10500 }
10501}
10502
aa0665e5
RN
10503/*
10504 * (Re)Calculate the bounds of the waypoints in this layer,
10505 * This should be called whenever waypoints are changed
10506 */
2cec1c4e 10507void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
aa0665e5
RN
10508{
10509 struct LatLon topleft = { 0.0, 0.0 };
10510 struct LatLon bottomright = { 0.0, 0.0 };
10511 struct LatLon ll;
10512
10513 GHashTableIter iter;
10514 gpointer key, value;
10515
10516 g_hash_table_iter_init ( &iter, vtl->waypoints );
10517
10518 // Set bounds to first point
10519 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10520 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10521 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10522 }
10523
10524 // Ensure there is another point...
10525 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10526
10527 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10528
10529 // See if this point increases the bounds.
10530 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10531
10532 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10533 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10534 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10535 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10536 }
10537 }
10538
10539 vtl->waypoints_bbox.north = topleft.lat;
10540 vtl->waypoints_bbox.east = bottomright.lon;
10541 vtl->waypoints_bbox.south = bottomright.lat;
10542 vtl->waypoints_bbox.west = topleft.lon;
10543}
10544
20981fd6 10545static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
b1453c16 10546{
20981fd6
RN
10547 vik_track_calculate_bounds ( trk );
10548}
10549
10550static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10551{
10552 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10553 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10554}
10555
c9cac058
RN
10556static void trw_layer_sort_all ( VikTrwLayer *vtl )
10557{
7a52aac6
RN
10558 if ( ! VIK_LAYER(vtl)->vt )
10559 return;
10560
c9cac058
RN
10561 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10562 if ( g_hash_table_size (vtl->tracks) > 1 )
10563 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10564
10565 if ( g_hash_table_size (vtl->routes) > 1 )
10566 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10567
10568 if ( g_hash_table_size (vtl->waypoints) > 1 )
10569 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10570}
10571
20981fd6
RN
10572static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10573{
626de648
RN
10574 if ( VIK_LAYER(vtl)->realized )
10575 trw_layer_verify_thumbnails ( vtl, vvp );
b1453c16 10576 trw_layer_track_alloc_colors ( vtl );
aa0665e5
RN
10577
10578 trw_layer_calculate_bounds_waypoints ( vtl );
20981fd6 10579 trw_layer_calculate_bounds_tracks ( vtl );
c9cac058
RN
10580
10581 // Apply treeview sort after loading all the tracks for this layer
10582 // (rather than sorted insert on each individual track additional)
10583 // and after subsequent changes to the properties as the specified order may have changed.
10584 // since the sorting of a treeview section is now very quick
10585 // NB sorting is also performed after every name change as well to maintain the list order
10586 trw_layer_sort_all ( vtl );
6ba42f1e
RN
10587
10588 // Setting metadata time if not otherwise set
10589 if ( vtl->metadata ) {
10590
10591 gboolean need_to_set_time = TRUE;
10592 if ( vtl->metadata->timestamp ) {
10593 need_to_set_time = FALSE;
10594 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10595 need_to_set_time = TRUE;
10596 }
10597
10598 if ( need_to_set_time ) {
10599 // Could rewrite this as a general get first time of a TRW Layer function
10600 GTimeVal timestamp;
10601 timestamp.tv_usec = 0;
10602 gboolean has_timestamp = FALSE;
10603
10604 GList *gl = NULL;
10605 gl = g_hash_table_get_values ( vtl->tracks );
10606 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10607 gl = g_list_first ( gl );
10608
10609 // Check times of tracks
10610 if ( gl ) {
10611 // Only need to check the first track as they have been sorted by time
10612 VikTrack *trk = (VikTrack*)gl->data;
10613 // Assume trackpoints already sorted by time
10614 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10615 if ( tpt && tpt->has_timestamp ) {
10616 timestamp.tv_sec = tpt->timestamp;
10617 has_timestamp = TRUE;
10618 }
10619 g_list_free ( gl );
10620 }
10621
10622 if ( !has_timestamp ) {
10623 // 'Last' resort - current time
10624 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10625 g_get_current_time ( &timestamp );
10626
10627 // Check times of waypoints
10628 gl = g_hash_table_get_values ( vtl->waypoints );
10629 GList *iter;
10630 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10631 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10632 if ( wpt->has_timestamp ) {
10633 if ( timestamp.tv_sec > wpt->timestamp ) {
10634 timestamp.tv_sec = wpt->timestamp;
10635 has_timestamp = TRUE;
10636 }
10637 }
10638 }
10639 g_list_free ( gl );
10640 }
10641
10642 vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10643 }
10644 }
b1453c16
RN
10645}
10646
50a14534
EB
10647VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10648{
10649 return vtl->coord_mode;
10650}
10651
073ede8c
RN
10652/**
10653 * Uniquify the whole layer
10654 * Also requires the layers panel as the names shown there need updating too
10655 * Returns whether the operation was successful or not
10656 */
10657gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10658{
10659 if ( vtl && vlp ) {
0d2b891f
RN
10660 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10661 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
073ede8c
RN
10662 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10663 return TRUE;
10664 }
10665 return FALSE;
10666}
50a14534 10667
c9570f86 10668static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
50a14534
EB
10669{
10670 vik_coord_convert ( &(wp->coord), *dest_mode );
10671}
10672
9e212bfc 10673static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
50a14534
EB
10674{
10675 vik_track_convert ( tr, *dest_mode );
10676}
10677
10678static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10679{
10680 if ( vtl->coord_mode != dest_mode )
10681 {
10682 vtl->coord_mode = dest_mode;
10683 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10684 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6440ca9b 10685 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
50a14534
EB
10686 }
10687}
e4afc73a 10688
a7cd93ac 10689static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
20c7a3a0
QT
10690{
10691 vtl->menu_selection = selection;
10692}
10693
a7cd93ac 10694static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
20c7a3a0 10695{
a7cd93ac 10696 return (vtl->menu_selection);
20c7a3a0
QT
10697}
10698
7114e879
QT
10699/* ----------- Downloading maps along tracks --------------- */
10700
10701static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10702{
10703 /* TODO: calculating based on current size of viewport */
10704 const gdouble w_at_zoom_0_125 = 0.0013;
10705 const gdouble h_at_zoom_0_125 = 0.0011;
10706 gdouble zoom_factor = zoom_level/0.125;
10707
10708 wh->lat = h_at_zoom_0_125 * zoom_factor;
10709 wh->lon = w_at_zoom_0_125 * zoom_factor;
10710
10711 return 0; /* all OK */
10712}
10713
35e22ed8
QT
10714static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10715{
10716 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10717 (dist->lat >= ABS(to->north_south - from->north_south)))
10718 return NULL;
10719
10720 VikCoord *coord = g_malloc(sizeof(VikCoord));
10721 coord->mode = VIK_COORD_LATLON;
10722
10723 if (ABS(gradient) < 1) {
10724 if (from->east_west > to->east_west)
10725 coord->east_west = from->east_west - dist->lon;
10726 else
10727 coord->east_west = from->east_west + dist->lon;
10728 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10729 } else {
10730 if (from->north_south > to->north_south)
10731 coord->north_south = from->north_south - dist->lat;
10732 else
10733 coord->north_south = from->north_south + dist->lat;
10734 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10735 }
10736
10737 return coord;
10738}
10739
10740static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10741{
10742 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10743 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10744
10745 VikCoord *next = from;
10746 while (TRUE) {
10747 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10748 break;
10749 list = g_list_prepend(list, next);
10750 }
10751
10752 return list;
10753}
10754
7114e879
QT
10755void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10756{
10757 typedef struct _Rect {
10758 VikCoord tl;
10759 VikCoord br;
35e22ed8 10760 VikCoord center;
7114e879 10761 } Rect;
35e22ed8 10762#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
10763
10764 struct LatLon wh;
35e22ed8 10765 GList *rects_to_download = NULL;
7114e879
QT
10766 GList *rect_iter;
10767
10768 if (get_download_area_width(vvp, zoom_level, &wh))
10769 return;
10770
10771 GList *iter = tr->trackpoints;
35e22ed8
QT
10772 if (!iter)
10773 return;
7114e879
QT
10774
10775 gboolean new_map = TRUE;
10776 VikCoord *cur_coord, tl, br;
10777 Rect *rect;
7114e879
QT
10778 while (iter) {
10779 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10780 if (new_map) {
10781 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10782 rect = g_malloc(sizeof(Rect));
10783 rect->tl = tl;
10784 rect->br = br;
35e22ed8
QT
10785 rect->center = *cur_coord;
10786 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
10787 new_map = FALSE;
10788 iter = iter->next;
10789 continue;
10790 }
10791 gboolean found = FALSE;
35e22ed8
QT
10792 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10793 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
10794 found = TRUE;
10795 break;
10796 }
10797 }
10798 if (found)
10799 iter = iter->next;
10800 else
10801 new_map = TRUE;
10802 }
35e22ed8 10803
35e22ed8 10804 GList *fillins = NULL;
b1e57d16
RN
10805 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10806 /* seems that ATM the function get_next_coord works only for LATLON */
10807 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10808 /* fill-ins for far apart points */
10809 GList *cur_rect, *next_rect;
10810 for (cur_rect = rects_to_download;
10811 (next_rect = cur_rect->next) != NULL;
10812 cur_rect = cur_rect->next) {
10813 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10814 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10815 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10816 }
35e22ed8 10817 }
3cbbb49e
GB
10818 } else
10819 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
10820
10821 if (fillins) {
c60f82df
RN
10822 GList *fiter = fillins;
10823 while (fiter) {
10824 cur_coord = (VikCoord *)(fiter->data);
35e22ed8
QT
10825 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10826 rect = g_malloc(sizeof(Rect));
10827 rect->tl = tl;
10828 rect->br = br;
10829 rect->center = *cur_coord;
10830 rects_to_download = g_list_prepend(rects_to_download, rect);
c60f82df 10831 fiter = fiter->next;
35e22ed8
QT
10832 }
10833 }
10834
10835 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3ac548fa 10836 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7114e879
QT
10837 }
10838
35e22ed8
QT
10839 if (fillins) {
10840 for (iter = fillins; iter; iter = iter->next)
10841 g_free(iter->data);
10842 g_list_free(fillins);
10843 }
10844 if (rects_to_download) {
10845 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10846 g_free(rect_iter->data);
10847 g_list_free(rects_to_download);
10848 }
7114e879
QT
10849}
10850
19782ffd 10851static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
7114e879
QT
10852{
10853 VikMapsLayer *vml;
fe7e4e45 10854 gint selected_map;
7114e879
QT
10855 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10856 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10857 gint selected_zoom, default_zoom;
7114e879 10858
19782ffd
RN
10859 VikTrwLayer *vtl = values[MA_VTL];
10860 VikLayersPanel *vlp = values[MA_VLP];
0d2b891f 10861 VikTrack *trk;
19782ffd
RN
10862 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10863 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 10864 else
19782ffd 10865 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
10866 if ( !trk )
10867 return;
10868
7114e879
QT
10869 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10870
aa7ed888 10871 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
10872 int num_maps = g_list_length(vmls);
10873
10874 if (!num_maps) {
4d333042 10875 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
7114e879
QT
10876 return;
10877 }
10878
fe7e4e45 10879 // Convert from list of vmls to list of names. Allowing the user to select one of them
7114e879
QT
10880 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10881 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10882
10883 gchar **np = map_names;
10884 VikMapsLayer **lp = map_layers;
fe7e4e45 10885 int i;
7114e879 10886 for (i = 0; i < num_maps; i++) {
7114e879 10887 vml = (VikMapsLayer *)(vmls->data);
fe7e4e45
RN
10888 *lp++ = vml;
10889 *np++ = vik_maps_layer_get_map_label(vml);
7114e879
QT
10890 vmls = vmls->next;
10891 }
fe7e4e45 10892 // Mark end of the array lists
7114e879
QT
10893 *lp = NULL;
10894 *np = NULL;
7114e879
QT
10895
10896 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
fe7e4e45 10897 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
7114e879
QT
10898 if (cur_zoom == zoom_vals[default_zoom])
10899 break;
10900 }
fe7e4e45 10901 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
7114e879 10902
fe7e4e45 10903 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
7114e879
QT
10904 goto done;
10905
0d2b891f 10906 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7114e879
QT
10907
10908done:
10909 for (i = 0; i < num_maps; i++)
10910 g_free(map_names[i]);
10911 g_free(map_names);
10912 g_free(map_layers);
10913
10914 g_list_free(vmls);
10915
10916}
0c1044e9 10917
a8fe53f8
EB
10918/**** lowest waypoint number calculation ***/
10919static gint highest_wp_number_name_to_number(const gchar *name) {
10920 if ( strlen(name) == 3 ) {
10921 int n = atoi(name);
10922 if ( n < 100 && name[0] != '0' )
10923 return -1;
10924 if ( n < 10 && name[0] != '0' )
10925 return -1;
10926 return n;
10927 }
10928 return -1;
10929}
10930
10931
10932static void highest_wp_number_reset(VikTrwLayer *vtl)
10933{
10934 vtl->highest_wp_number = -1;
10935}
10936
10937static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10938{
10939 /* if is bigger that top, add it */
10940 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10941 if ( new_wp_num > vtl->highest_wp_number )
10942 vtl->highest_wp_number = new_wp_num;
10943}
10944
10945static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10946{
10947 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10948 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10949 if ( vtl->highest_wp_number == old_wp_num ) {
10950 gchar buf[4];
c9570f86 10951 vtl->highest_wp_number--;
a8fe53f8
EB
10952
10953 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10954 /* search down until we find something that *does* exist */
10955
c9570f86
RN
10956 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10957 vtl->highest_wp_number--;
a8fe53f8
EB
10958 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10959 }
10960 }
10961}
10962
10963/* get lowest unused number */
10964static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10965{
10966 gchar buf[4];
10967 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 10968 return NULL;
a8fe53f8
EB
10969 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10970 return g_strdup(buf);
10971}
07c9d42b
RN
10972
10973/**
10974 * trw_layer_create_track_list_both:
10975 *
10976 * Create the latest list of tracks and routes
10977 */
10978static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10979{
10980 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10981 GList *tracks = NULL;
10982 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10983 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10984
10985 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10986}
10987
19782ffd 10988static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
07c9d42b 10989{
19782ffd 10990 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10991
10992 gchar *title = NULL;
19782ffd 10993 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
07c9d42b
RN
10994 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10995 else
10996 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10997
19782ffd 10998 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
07c9d42b
RN
10999 g_free ( title );
11000}
11001
1c2083ba 11002static void trw_layer_track_list_dialog ( menu_array_layer values )
07c9d42b 11003{
1c2083ba 11004 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
11005
11006 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
11007 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
11008 g_free ( title );
11009}
00176e85 11010
1c2083ba 11011static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
00176e85 11012{
1c2083ba 11013 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
00176e85
RN
11014
11015 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
11016 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
11017 g_free ( title );
11018}