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