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