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