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