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