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