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