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