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