]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
Add tooltips for Preferences and Layer Parameters.
[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 */
50a14534 25/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
0d2b891f 26/* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
50a14534 27
3e7553ae
GB
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
50a14534 32#include "viking.h"
7114e879 33#include "vikmapslayer.h"
e50758c7 34#include "vikgpslayer.h"
50a14534
EB
35#include "viktrwlayer_tpwin.h"
36#include "viktrwlayer_propwin.h"
b3eb3b98
RN
37#ifdef VIK_CONFIG_GEOTAG
38#include "viktrwlayer_geotag.h"
39#include "geotag_exif.h"
40#endif
acaf7113 41#include "garminsymbols.h"
50a14534
EB
42#include "thumbnails.h"
43#include "background.h"
911400b5 44#include "gpx.h"
e0b0b9c1 45#include "babel.h"
ad0a8c2d
EB
46#include "dem.h"
47#include "dems.h"
165a4fa9 48#include "geonamessearch.h"
3e7553ae
GB
49#ifdef VIK_CONFIG_OPENSTREETMAP
50#include "osm-traces.h"
51#endif
28c82d8b 52#include "acquire.h"
16fc32f6 53#include "datasources.h"
e50758c7 54#include "datasource_gps.h"
7d02a0b0 55#include "util.h"
50a14534 56
bce3a7b0
EB
57#include "icons/icons.h"
58
8c00358d 59#ifdef HAVE_MATH_H
50a14534 60#include <math.h>
8c00358d
GB
61#endif
62#ifdef HAVE_STRING_H
50a14534 63#include <string.h>
8c00358d
GB
64#endif
65#ifdef HAVE_STDLIB_H
50a14534 66#include <stdlib.h>
8c00358d 67#endif
8c060406 68#include <stdio.h>
50a14534
EB
69#include <ctype.h>
70
777e2d4d 71#include <gdk/gdkkeysyms.h>
8c060406
MA
72#include <glib.h>
73#include <glib/gstdio.h>
4c77d5e0 74#include <glib/gi18n.h>
777e2d4d 75
55340efa
GB
76#ifdef VIK_CONFIG_GOOGLE
77#define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
ebf1bd39
RN
78#endif
79
b1453c16
RN
80#define VIK_TRW_LAYER_TRACK_GC 5
81#define VIK_TRW_LAYER_TRACK_GCS 10
82#define VIK_TRW_LAYER_TRACK_GC_BLACK 0
83#define VIK_TRW_LAYER_TRACK_GC_SLOW 1
84#define VIK_TRW_LAYER_TRACK_GC_AVER 2
85#define VIK_TRW_LAYER_TRACK_GC_FAST 3
86#define VIK_TRW_LAYER_TRACK_GC_STOP 4
50a14534
EB
87
88#define DRAWMODE_BY_TRACK 0
18df6d42 89#define DRAWMODE_BY_SPEED 1
50a14534 90#define DRAWMODE_ALL_BLACK 2
18df6d42
RN
91// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
92// as we are (re)calculating the colour for every point
50a14534
EB
93
94#define POINTS 1
95#define LINES 2
96
97/* this is how it knows when you click if you are clicking close to a trackpoint. */
98#define TRACKPOINT_SIZE_APPROX 5
99#define WAYPOINT_SIZE_APPROX 5
100
b42a25ba
EB
101#define MIN_STOP_LENGTH 15
102#define MAX_STOP_LENGTH 86400
103#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
105
106enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
107
5a77ae71
RN
108// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
109typedef enum {
110 FS_XX_SMALL = 0, // 'xx-small'
111 FS_X_SMALL,
112 FS_SMALL,
113 FS_MEDIUM, // DEFAULT
114 FS_LARGE,
115 FS_X_LARGE,
116 FS_XX_LARGE,
117 FS_NUM_SIZES
118} font_size_t;
119
50a14534
EB
120struct _VikTrwLayer {
121 VikLayer vl;
122 GHashTable *tracks;
123 GHashTable *tracks_iters;
0d2b891f
RN
124 GHashTable *routes;
125 GHashTable *routes_iters;
50a14534
EB
126 GHashTable *waypoints_iters;
127 GHashTable *waypoints;
0d2b891f
RN
128 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
129 gboolean tracks_visible, routes_visible, waypoints_visible;
50a14534
EB
130 guint8 drawmode;
131 guint8 drawpoints;
3a1c8e42 132 guint8 drawpoints_size;
b42a25ba
EB
133 guint8 drawelevation;
134 guint8 elevation_factor;
135 guint8 drawstops;
136 guint32 stop_length;
50a14534 137 guint8 drawlines;
08084371 138 guint8 drawdirections;
3a1c8e42 139 guint8 drawdirections_size;
50a14534
EB
140 guint8 line_thickness;
141 guint8 bg_line_thickness;
142
143 guint8 wp_symbol;
144 guint8 wp_size;
ea3933fc 145 gboolean wp_draw_symbols;
5a77ae71 146 font_size_t wp_font_size;
50a14534 147
18df6d42 148 gdouble track_draw_speed_factor;
50a14534 149 GArray *track_gc;
b1453c16 150 GdkGC *track_1color_gc;
8e9c992d 151 GdkGC *current_track_gc;
745fda83
RN
152 // Separate GC for a track's potential new point as drawn via separate method
153 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
154 GdkGC *current_track_newpoint_gc;
50a14534
EB
155 GdkGC *track_bg_gc;
156 GdkGC *waypoint_gc;
157 GdkGC *waypoint_text_gc;
158 GdkGC *waypoint_bg_gc;
0d2b891f
RN
159 GdkFont *waypoint_font;
160 VikTrack *current_track; // ATM shared between new tracks and new routes
50a14534 161 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
745fda83 162 gboolean draw_sync_done;
ef5e8132 163 gboolean draw_sync_do;
50a14534
EB
164
165 VikCoordMode coord_mode;
166
167 /* wp editing tool */
168 VikWaypoint *current_wp;
c9570f86 169 gpointer current_wp_id;
50a14534
EB
170 gboolean moving_wp;
171 gboolean waypoint_rightclick;
172
173 /* track editing tool */
174 GList *current_tpl;
ce4bd1cf
RN
175 VikTrack *current_tp_track;
176 gpointer current_tp_id;
50a14534
EB
177 VikTrwLayerTpwin *tpwin;
178
50a14534
EB
179 /* track editing tool -- more specifically, moving tps */
180 gboolean moving_tp;
181
7ff7d728
RN
182 /* route finder tool */
183 gboolean route_finder_started;
184 VikCoord route_finder_coord;
185 gboolean route_finder_check_added_track;
ce4bd1cf 186 VikTrack *route_finder_added_track;
7ff7d728
RN
187 VikTrack *route_finder_current_track;
188 gboolean route_finder_append;
1eef1bde 189
50a14534
EB
190 gboolean drawlabels;
191 gboolean drawimages;
192 guint8 image_alpha;
193 GQueue *image_cache;
194 guint8 image_size;
195 guint16 image_cache_size;
196
197 /* for waypoint text */
198 PangoLayout *wplabellayout;
199
200 gboolean has_verified_thumbnails;
201
202 GtkMenu *wp_right_click_menu;
8bd81489 203 GtkMenu *track_right_click_menu;
50a14534 204
20c7a3a0
QT
205 /* menu */
206 VikStdLayerMenuItem menu_selection;
207
a8fe53f8 208 gint highest_wp_number;
50a14534
EB
209};
210
211/* A caached waypoint image. */
212typedef struct {
213 GdkPixbuf *pixbuf;
214 gchar *image; /* filename */
215} CachedPixbuf;
216
217struct DrawingParams {
218 VikViewport *vp;
219 VikTrwLayer *vtl;
220 gdouble xmpp, ympp;
221 guint16 width, height;
08084371
RN
222 gdouble cc; // Cosine factor in track directions
223 gdouble ss; // Sine factor in track directions
50a14534 224 const VikCoord *center;
50a14534
EB
225 gboolean one_zone, lat_lon;
226 gdouble ce1, ce2, cn1, cn2;
227};
228
c9570f86
RN
229static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
230
6bb72350
RN
231static void trw_layer_delete_item ( gpointer pass_along[6] );
232static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
233static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
33534cd8 234
50a14534 235static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
867f4673 236static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
165a4fa9 237static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
50a14534
EB
238
239static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
240static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
241
50a14534
EB
242static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
243static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
244
6bb72350
RN
245static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
246static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
50a14534 247static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
6bb72350
RN
248static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
249static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
250static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
251static void trw_layer_goto_track_center ( gpointer pass_along[6] );
24774c43 252static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
111fa174 253static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
6bb72350 254static void trw_layer_merge_with_other ( gpointer pass_along[6] );
9c34f614 255static void trw_layer_append_track ( gpointer pass_along[6] );
111fa174 256static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
af2341f3 257static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
a4aefe69 258static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
98e87078 259static void trw_layer_split_segments ( gpointer pass_along[6] );
b6eda120 260static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
6579ca1f 261static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
eb9fd106 262static void trw_layer_reverse ( gpointer pass_along[6] );
c95d6b00
RN
263static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
264static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
a412f3f5 265static void trw_layer_show_picture ( gpointer pass_along[6] );
e50758c7 266static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
c95d6b00 267
50a14534 268static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
5a10c240 269static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
ce4bd1cf 270static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
50a14534
EB
271static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
272static void trw_layer_new_wp ( gpointer lav[2] );
37615c52 273static void trw_layer_new_track ( gpointer lav[2] );
0d2b891f 274static void trw_layer_new_route ( gpointer lav[2] );
37615c52 275static void trw_layer_finish_track ( gpointer lav[2] );
fc59e8c7 276static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
535ed1ae 277static void trw_layer_auto_tracks_view ( gpointer lav[2] );
c9a5cbf9 278static void trw_layer_delete_all_tracks ( gpointer lav[2] );
20b671c3 279static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
c9a5cbf9 280static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
20b671c3 281static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
165a4fa9
HR
282static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
283static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
b3eb3b98
RN
284#ifdef VIK_CONFIG_GEOTAG
285static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
286static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
287static void trw_layer_geotagging_track ( gpointer pass_along[6] );
288static void trw_layer_geotagging ( gpointer lav[2] );
289#endif
16fc32f6 290static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
55340efa 291#ifdef VIK_CONFIG_GOOGLE
16fc32f6 292static void trw_layer_acquire_google_cb ( gpointer lav[2] );
29332250 293#endif
40f5740b
RN
294#ifdef VIK_CONFIG_OPENSTREETMAP
295static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
296#endif
16fc32f6
RN
297#ifdef VIK_CONFIG_GEOCACHES
298static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
299#endif
68bab1bd
RN
300#ifdef VIK_CONFIG_GEOTAG
301static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
302#endif
d7ac7564 303static void trw_layer_acquire_file_cb ( gpointer lav[2] );
e50758c7 304static void trw_layer_gps_upload ( gpointer lav[2] );
50a14534 305
0d2b891f
RN
306// Specific route versions:
307// Most track handling functions can handle operating on the route list
308// However these ones are easier in separate functions
309static void trw_layer_auto_routes_view ( gpointer lav[2] );
310static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
311
50a14534 312/* pop-up items */
c9570f86 313static void trw_layer_properties_item ( gpointer pass_along[7] );
6bb72350
RN
314static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
315static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
50a14534 316
c9570f86 317static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
ce4bd1cf 318static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
08084371 319static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
50a14534 320
db79f75f 321static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
50a14534
EB
322static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
323static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
324static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 325
941aa6e9 326static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
33534cd8 327static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 328static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
33534cd8 329static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
330static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
331static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
332static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
7432fddf 333static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 334static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
7432fddf 335static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
e37b2a6d
RN
336static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
337static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
338static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
339static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
dc2c040e 340static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
ef5e8132 341static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
777e2d4d 342static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
941aa6e9
AF
343static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
344static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
55340efa 345#ifdef VIK_CONFIG_GOOGLE
7ff7d728
RN
346static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
347static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
ebf1bd39 348#endif
50a14534 349
50a14534
EB
350static void cached_pixbuf_free ( CachedPixbuf *cp );
351static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
50a14534
EB
352
353static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
354static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
355
c9570f86 356static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
ddc47a46 357static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 358
a8fe53f8
EB
359static gchar *highest_wp_number_get(VikTrwLayer *vtl);
360static void highest_wp_number_reset(VikTrwLayer *vtl);
361static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
362static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
363
79dce0cb
RN
364// Note for the following tool GtkRadioActionEntry texts:
365// the very first text value is an internal name not displayed anywhere
366// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
367// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
368// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
369// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
50a14534 370static VikToolInterface trw_layer_tools[] = {
79dce0cb
RN
371 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
372 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
ef5e8132
RN
373 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
374 FALSE,
375 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
941aa6e9 376
79dce0cb
RN
377 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
378 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
ef5e8132
RN
379 (VikToolMouseFunc) tool_new_track_click,
380 (VikToolMouseMoveFunc) tool_new_track_move,
381 (VikToolMouseFunc) tool_new_track_release,
382 (VikToolKeyFunc) tool_new_track_key_press,
383 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
384 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
50a14534 385
e37b2a6d
RN
386 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
387 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
388 (VikToolMouseFunc) tool_new_route_click,
389 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
390 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
391 (VikToolKeyFunc) tool_new_track_key_press, // -/#
392 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
393 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
98f5364d 394
79dce0cb
RN
395 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
396 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
7432fddf 397 (VikToolMouseFunc) tool_edit_waypoint_click,
dc2c040e 398 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
ef5e8132
RN
399 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
400 FALSE,
401 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
941aa6e9 402
79dce0cb
RN
403 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
404 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
33534cd8 405 (VikToolMouseFunc) tool_edit_trackpoint_click,
dc2c040e 406 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
ef5e8132
RN
407 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
408 FALSE,
409 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
941aa6e9 410
79dce0cb
RN
411 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
412 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
ef5e8132
RN
413 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
414 FALSE,
415 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
1eef1bde 416
55340efa 417#ifdef VIK_CONFIG_GOOGLE
79dce0cb
RN
418 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
419 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
ef5e8132
RN
420 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
421 FALSE,
422 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
ebf1bd39 423#endif
941aa6e9 424};
e37b2a6d 425enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_CREATE_ROUTE, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
50a14534
EB
426
427/****** PARAMETERS ******/
428
4c77d5e0 429static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
55906514 430enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
50a14534 431
18df6d42 432static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
4c77d5e0 433static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
50a14534 434
3a1c8e42
RN
435#define MIN_POINT_SIZE 2
436#define MAX_POINT_SIZE 10
437
438#define MIN_ARROW_SIZE 3
439#define MAX_ARROW_SIZE 20
b42a25ba 440
50a14534
EB
441static VikLayerParamScale params_scales[] = {
442 /* min max step digits */
443 { 1, 10, 1, 0 }, /* line_thickness */
18df6d42
RN
444 { 0, 100, 1, 0 }, /* track draw speed factor */
445 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
50a14534 446 /* 5 * step == how much to turn */
3a1c8e42
RN
447 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
448 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
449 { 5, 500, 5, 0 }, // 5: image cache_size - " "
450 { 0, 8, 1, 0 }, // 6: Background line thickness
50a14534 451 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba 452 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
3a1c8e42
RN
453 { 1, 100, 1, 0 }, // 9: elevation factor
454 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
455 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
50a14534
EB
456};
457
5a77ae71
RN
458static gchar* params_font_sizes[] = {
459 N_("Extra Extra Small"),
460 N_("Extra Small"),
461 N_("Small"),
462 N_("Medium"),
463 N_("Large"),
464 N_("Extra Large"),
465 N_("Extra Extra Large"),
466 NULL };
467
50a14534 468VikLayerParam trw_layer_params[] = {
e6994d8d
RN
469 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
470 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
471 { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL },
472
473 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL, NULL },
474 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
475 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL },
476 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
477 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL },
478 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
479 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL },
480 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
481 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL },
482
483 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
484 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time") },
485 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL },
486
487 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL},
488 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
489 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
490 N_("The percentage factor away from the average speed determining the color used") },
491
492 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
493 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL },
494 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
495 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
496 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL },
497 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
498 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL, NULL, NULL },
499 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL },
500 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
501
502 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL },
503 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL },
504 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL },
505 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL },
50a14534
EB
506};
507
3a1c8e42 508// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
08084371
RN
509enum {
510 // Sublayer visibilities
511 PARAM_TV,
512 PARAM_WV,
0d2b891f 513 PARAM_RV,
08084371
RN
514 // Tracks
515 PARAM_DM,
516 PARAM_DL,
3a1c8e42 517 PARAM_LT,
08084371 518 PARAM_DD,
3a1c8e42 519 PARAM_DDS,
08084371 520 PARAM_DP,
3a1c8e42 521 PARAM_DPS,
08084371
RN
522 PARAM_DE,
523 PARAM_EF,
524 PARAM_DS,
525 PARAM_SL,
08084371
RN
526 PARAM_BLT,
527 PARAM_TBGC,
528 PARAM_TDSF,
08084371 529 // Waypoints
3a1c8e42 530 PARAM_DLA,
08084371
RN
531 PARAM_WPFONTSIZE,
532 PARAM_WPC,
533 PARAM_WPTC,
534 PARAM_WPBC,
535 PARAM_WPBA,
536 PARAM_WPSYM,
537 PARAM_WPSIZE,
538 PARAM_WPSYMS,
539 // WP images
540 PARAM_DI,
541 PARAM_IS,
542 PARAM_IA,
543 PARAM_ICS,
544 NUM_PARAMS
545};
ad0a8c2d
EB
546
547/*** TO ADD A PARAM:
548 *** 1) Add to trw_layer_params and enumeration
549 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
ad0a8c2d 550 ***/
50a14534
EB
551
552/****** END PARAMETERS ******/
553
a7cd93ac
RN
554static VikTrwLayer* trw_layer_new ( gint drawmode );
555/* Layer Interface function definitions */
556static VikTrwLayer* trw_layer_create ( VikViewport *vp );
557static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
b1453c16 558static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp );
a7cd93ac
RN
559static void trw_layer_free ( VikTrwLayer *trwlayer );
560static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
561static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
562static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
563static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
564static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
565static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
566static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
567static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
568static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
569static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
570static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
571static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
572static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
573static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
574static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
575static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
576static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
577static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
578static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
579static void trw_layer_free_copied_item ( gint subtype, gpointer item );
580static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
581static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
582static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
583static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
584static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
585/* End Layer Interface function definitions */
586
50a14534 587VikLayerInterface vik_trw_layer_interface = {
db386630 588 "TrackWaypoint",
affcc0f2 589 N_("TrackWaypoint"),
75078768 590 "<control><shift>Y",
5bfafde9 591 &viktrwlayer_pixbuf,
50a14534
EB
592
593 trw_layer_tools,
594 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
595
596 trw_layer_params,
597 NUM_PARAMS,
598 params_groups, /* params_groups */
599 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
600
5a4a28bf
QT
601 VIK_MENU_ITEM_ALL,
602
a7cd93ac
RN
603 (VikLayerFuncCreate) trw_layer_create,
604 (VikLayerFuncRealize) trw_layer_realize,
b1453c16 605 (VikLayerFuncPostRead) trw_layer_post_read,
a7cd93ac 606 (VikLayerFuncFree) trw_layer_free,
50a14534
EB
607
608 (VikLayerFuncProperties) NULL,
a7cd93ac 609 (VikLayerFuncDraw) trw_layer_draw,
50a14534
EB
610 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
611
a7cd93ac
RN
612 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
613 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
20c7a3a0 614
a7cd93ac
RN
615 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
616 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
50a14534 617
a7cd93ac
RN
618 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
619 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
c7060c4e 620 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
cb89c5a5 621 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
a7cd93ac 622 (VikLayerFuncLayerSelected) trw_layer_selected,
50a14534 623
911400b5
AF
624 (VikLayerFuncMarshall) trw_layer_marshall,
625 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
626
627 (VikLayerFuncSetParam) trw_layer_set_param,
628 (VikLayerFuncGetParam) trw_layer_get_param,
629
630 (VikLayerFuncReadFileData) a_gpspoint_read_file,
631 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
632
33534cd8 633 (VikLayerFuncDeleteItem) trw_layer_del_item,
d5874ef9 634 (VikLayerFuncCutItem) trw_layer_cut_item,
50a14534
EB
635 (VikLayerFuncCopyItem) trw_layer_copy_item,
636 (VikLayerFuncPasteItem) trw_layer_paste_item,
637 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
638
639 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
77ad64fa
RN
640
641 (VikLayerFuncSelectClick) trw_layer_select_click,
08f14055
RN
642 (VikLayerFuncSelectMove) trw_layer_select_move,
643 (VikLayerFuncSelectRelease) trw_layer_select_release,
e46f259a 644 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
50a14534
EB
645};
646
50a14534
EB
647GType vik_trw_layer_get_type ()
648{
649 static GType vtl_type = 0;
650
651 if (!vtl_type)
652 {
653 static const GTypeInfo vtl_info =
654 {
655 sizeof (VikTrwLayerClass),
656 NULL, /* base_init */
657 NULL, /* base_finalize */
658 NULL, /* class init */
659 NULL, /* class_finalize */
660 NULL, /* class_data */
661 sizeof (VikTrwLayer),
662 0,
663 NULL /* instance init */
664 };
665 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
666 }
667
668 return vtl_type;
669}
670
33534cd8
AF
671static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
672{
6bb72350 673 static gpointer pass_along[6];
33534cd8
AF
674 if (!sublayer) {
675 return;
676 }
677
678 pass_along[0] = vtl;
679 pass_along[1] = NULL;
dc2c040e 680 pass_along[2] = GINT_TO_POINTER (subtype);
33534cd8 681 pass_along[3] = sublayer;
169acf64 682 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6bb72350 683 pass_along[5] = NULL;
33534cd8
AF
684
685 trw_layer_delete_item ( pass_along );
686}
50a14534 687
d5874ef9
RN
688static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
689{
6bb72350 690 static gpointer pass_along[6];
d5874ef9
RN
691 if (!sublayer) {
692 return;
693 }
694
695 pass_along[0] = vtl;
696 pass_along[1] = NULL;
697 pass_along[2] = GINT_TO_POINTER (subtype);
698 pass_along[3] = sublayer;
169acf64 699 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
6bb72350 700 pass_along[5] = NULL;
d5874ef9
RN
701
702 trw_layer_copy_item_cb(pass_along);
703 trw_layer_cut_item_cb(pass_along);
704}
705
6bb72350 706static void trw_layer_copy_item_cb ( gpointer pass_along[6])
2cebc318
QT
707{
708 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 709 gint subtype = GPOINTER_TO_INT (pass_along[2]);
2cebc318
QT
710 gpointer * sublayer = pass_along[3];
711 guint8 *data = NULL;
712 guint len;
713
714 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
715
716 if (data) {
c9570f86
RN
717 const gchar* name;
718 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
719 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
720 if ( wp && wp->name )
721 name = wp->name;
722 else
723 name = NULL; // Broken :(
724 }
0d2b891f 725 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
ce4bd1cf
RN
726 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
727 if ( trk && trk->name )
728 name = trk->name;
729 else
730 name = NULL; // Broken :(
731 }
0d2b891f
RN
732 else {
733 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
734 if ( trk && trk->name )
735 name = trk->name;
736 else
737 name = NULL; // Broken :(
738 }
c9570f86 739
2cebc318 740 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
c9570f86 741 subtype, len, name, data);
2cebc318
QT
742 }
743}
744
6bb72350 745static void trw_layer_cut_item_cb ( gpointer pass_along[6])
2cebc318
QT
746{
747 trw_layer_copy_item_cb(pass_along);
169acf64 748 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
2cebc318
QT
749 trw_layer_delete_item(pass_along);
750}
751
0d80642e
RN
752static void trw_layer_paste_item_cb ( gpointer pass_along[6])
753{
754 // Slightly cheating method, routing via the panels capability
755 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
756}
757
ddc47a46 758static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 759{
ddc47a46
AF
760 guint8 *id;
761 guint il;
762
763 if (!sublayer) {
764 *item = NULL;
765 return;
50a14534 766 }
ddc47a46 767
a50640dd
RN
768 GByteArray *ba = g_byte_array_new ();
769
770 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
ddc47a46 771 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
0d2b891f 772 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
ddc47a46 773 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
0d2b891f
RN
774 } else {
775 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
50a14534 776 }
ddc47a46 777
a50640dd
RN
778 g_byte_array_append ( ba, id, il );
779
ddc47a46 780 g_free(id);
a50640dd
RN
781
782 *len = ba->len;
783 *item = ba->data;
50a14534
EB
784}
785
ddc47a46 786static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 787{
a50640dd
RN
788 if ( !item )
789 return FALSE;
ddc47a46 790
a50640dd
RN
791 gchar *name;
792
793 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 794 {
ddc47a46 795 VikWaypoint *w;
ddc47a46 796
a50640dd 797 w = vik_waypoint_unmarshall ( item, len );
c9570f86 798 // When copying - we'll create a new name based on the original
9748531a 799 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
ddc47a46 800 vik_trw_layer_add_waypoint ( vtl, name, w );
c9570f86
RN
801 waypoint_convert (NULL, w, &vtl->coord_mode);
802
58cd1c43
RN
803 // Consider if redraw necessary for the new item
804 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
f01eebd4 805 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
806 return TRUE;
807 }
a50640dd 808 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 809 {
ddc47a46 810 VikTrack *t;
ce4bd1cf 811
a50640dd 812 t = vik_track_unmarshall ( item, len );
ce4bd1cf 813 // When copying - we'll create a new name based on the original
9748531a 814 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
ddc47a46 815 vik_trw_layer_add_track ( vtl, name, t );
0d2b891f 816 vik_track_convert (t, vtl->coord_mode);
ce4bd1cf 817
58cd1c43
RN
818 // Consider if redraw necessary for the new item
819 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
f01eebd4 820 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
821 return TRUE;
822 }
a50640dd 823 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
824 {
825 VikTrack *t;
0d2b891f 826
a50640dd 827 t = vik_track_unmarshall ( item, len );
0d2b891f
RN
828 // When copying - we'll create a new name based on the original
829 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
830 vik_trw_layer_add_route ( vtl, name, t );
831 vik_track_convert (t, vtl->coord_mode);
832
833 // Consider if redraw necessary for the new item
834 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
835 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
836 return TRUE;
837 }
50a14534
EB
838 return FALSE;
839}
840
841static void trw_layer_free_copied_item ( gint subtype, gpointer item )
842{
ddc47a46
AF
843 if (item) {
844 g_free(item);
50a14534
EB
845 }
846}
847
158b3642 848static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
50a14534
EB
849{
850 switch ( id )
851 {
852 case PARAM_TV: vtl->tracks_visible = data.b; break;
853 case PARAM_WV: vtl->waypoints_visible = data.b; break;
0d2b891f 854 case PARAM_RV: vtl->routes_visible = data.b; break;
50a14534
EB
855 case PARAM_DM: vtl->drawmode = data.u; break;
856 case PARAM_DP: vtl->drawpoints = data.b; break;
3a1c8e42
RN
857 case PARAM_DPS:
858 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
859 vtl->drawpoints_size = data.u;
860 break;
b42a25ba
EB
861 case PARAM_DE: vtl->drawelevation = data.b; break;
862 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 863 case PARAM_DL: vtl->drawlines = data.b; break;
08084371 864 case PARAM_DD: vtl->drawdirections = data.b; break;
3a1c8e42
RN
865 case PARAM_DDS:
866 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
867 vtl->drawdirections_size = data.u;
868 break;
b42a25ba
EB
869 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
870 vtl->stop_length = data.u;
871 break;
872 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
873 vtl->elevation_factor = data.u;
874 break;
50a14534
EB
875 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
876 {
877 vtl->line_thickness = data.u;
878 trw_layer_new_track_gcs ( vtl, vp );
879 }
880 break;
f53c5b13 881 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
50a14534
EB
882 {
883 vtl->bg_line_thickness = data.u;
884 trw_layer_new_track_gcs ( vtl, vp );
885 }
886 break;
50a14534 887 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
18df6d42 888 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
50a14534
EB
889 case PARAM_DLA: vtl->drawlabels = data.b; break;
890 case PARAM_DI: vtl->drawimages = data.b; break;
891 case PARAM_IS: if ( data.u != vtl->image_size )
892 {
893 vtl->image_size = data.u;
894 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
895 g_queue_free ( vtl->image_cache );
896 vtl->image_cache = g_queue_new ();
897 }
898 break;
899 case PARAM_IA: vtl->image_alpha = data.u; break;
900 case PARAM_ICS: vtl->image_cache_size = data.u;
901 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
902 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
903 break;
904 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
905 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
906 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
907 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
908 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
909 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 910 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
5a77ae71 911 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
50a14534
EB
912 }
913 return TRUE;
914}
915
158b3642 916static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
50a14534
EB
917{
918 VikLayerParamData rv;
919 switch ( id )
920 {
921 case PARAM_TV: rv.b = vtl->tracks_visible; break;
922 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
0d2b891f 923 case PARAM_RV: rv.b = vtl->routes_visible; break;
50a14534
EB
924 case PARAM_DM: rv.u = vtl->drawmode; break;
925 case PARAM_DP: rv.b = vtl->drawpoints; break;
3a1c8e42 926 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
b42a25ba
EB
927 case PARAM_DE: rv.b = vtl->drawelevation; break;
928 case PARAM_EF: rv.u = vtl->elevation_factor; break;
929 case PARAM_DS: rv.b = vtl->drawstops; break;
930 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534 931 case PARAM_DL: rv.b = vtl->drawlines; break;
08084371 932 case PARAM_DD: rv.b = vtl->drawdirections; break;
3a1c8e42 933 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
50a14534
EB
934 case PARAM_LT: rv.u = vtl->line_thickness; break;
935 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
50a14534
EB
936 case PARAM_DLA: rv.b = vtl->drawlabels; break;
937 case PARAM_DI: rv.b = vtl->drawimages; break;
938 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
18df6d42 939 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
50a14534
EB
940 case PARAM_IS: rv.u = vtl->image_size; break;
941 case PARAM_IA: rv.u = vtl->image_alpha; break;
942 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
943 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
944 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
945 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
946 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
947 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
948 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 949 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
5a77ae71 950 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
50a14534
EB
951 }
952 return rv;
953}
954
911400b5
AF
955static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
956{
4efcd55c 957 guint8 *pd;
4efcd55c 958 gint pl;
911400b5
AF
959
960 *data = NULL;
961
7b956cdd
RN
962 // Use byte arrays to store sublayer data
963 // much like done elsewhere e.g. vik_layer_marshall_params()
964 GByteArray *ba = g_byte_array_new ( );
965
966 guint8 *sl_data;
967 guint sl_len;
968
969 guint object_length;
970 guint subtype;
971 // store:
972 // the length of the item
973 // the sublayer type of item
974 // the the actual item
975#define tlm_append(object_pointer, size, type) \
976 subtype = (type); \
977 object_length = (size); \
978 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
979 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
980 g_byte_array_append ( ba, (object_pointer), object_length );
981
982 // Layer parameters first
983 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
984 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
985 g_byte_array_append ( ba, pd, pl );
986 g_free ( pd );
987
988 // Now sublayer data
989 GHashTableIter iter;
990 gpointer key, value;
991
992 // Waypoints
993 g_hash_table_iter_init ( &iter, vtl->waypoints );
994 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
995 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
996 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
997 g_free ( sl_data );
998 }
999
1000 // Tracks
1001 g_hash_table_iter_init ( &iter, vtl->tracks );
1002 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1003 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1004 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1005 g_free ( sl_data );
911400b5 1006 }
7b956cdd
RN
1007
1008 // Routes
1009 g_hash_table_iter_init ( &iter, vtl->routes );
1010 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1011 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1012 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1013 g_free ( sl_data );
1014 }
1015
1016#undef tlm_append
1017
1018 *data = ba->data;
1019 *len = ba->len;
911400b5
AF
1020}
1021
28612684 1022static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
911400b5 1023{
7b956cdd 1024 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
28612684 1025 gint pl;
7b956cdd 1026 gint consumed_length;
911400b5 1027
7b956cdd 1028 // First the overall layer parameters
911400b5
AF
1029 memcpy(&pl, data, sizeof(pl));
1030 data += sizeof(pl);
7b956cdd 1031 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
911400b5
AF
1032 data += pl;
1033
7b956cdd
RN
1034 consumed_length = pl;
1035 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1036
1037#define tlm_size (*(gint *)data)
1038 // See marshalling above for order of how this is written
1039#define tlm_next \
1040 data += sizeof_len_and_subtype + tlm_size;
1041
1042 // Now the individual sublayers:
1043
1044 while ( *data && consumed_length < len ) {
1045 // Normally four extra bytes at the end of the datastream
1046 // (since it's a GByteArray and that's where it's length is stored)
1047 // So only attempt read when there's an actual block of sublayer data
1048 if ( consumed_length + tlm_size < len ) {
1049
1050 // Reuse pl to read the subtype from the data stream
1051 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1052
1053 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1054 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1055 gchar *name = g_strdup ( trk->name );
1056 vik_trw_layer_add_track ( vtl, name, trk );
1057 g_free ( name );
1058 }
1059 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1060 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1061 gchar *name = g_strdup ( wp->name );
1062 vik_trw_layer_add_waypoint ( vtl, name, wp );
1063 g_free ( name );
1064 }
1065 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1066 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1067 gchar *name = g_strdup ( trk->name );
1068 vik_trw_layer_add_route ( vtl, name, trk );
1069 g_free ( name );
1070 }
1071 }
1072 consumed_length += tlm_size + sizeof_len_and_subtype;
1073 tlm_next;
911400b5 1074 }
7b956cdd
RN
1075 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1076
1077 return vtl;
911400b5
AF
1078}
1079
c9570f86
RN
1080// Keep interesting hash function at least visible
1081/*
8499a412
QT
1082static guint strcase_hash(gconstpointer v)
1083{
c9570f86 1084 // 31 bit hash function
8499a412
QT
1085 int i;
1086 const gchar *t = v;
c9570f86 1087 gchar s[128]; // malloc is too slow for reading big files
8499a412
QT
1088 gchar *p = s;
1089
1090 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1091 p[i] = toupper(t[i]);
1092 p[i] = '\0';
1093
1094 p = s;
1095 guint32 h = *p;
1096 if (h) {
1097 for (p += 1; *p != '\0'; p++)
1098 h = (h << 5) - h + *p;
1099 }
1100
1101 return h;
1102}
c9570f86 1103*/
8499a412 1104
a7cd93ac 1105static VikTrwLayer* trw_layer_new ( gint drawmode )
50a14534 1106{
267b6db5 1107 if (trw_layer_params[PARAM_DM].widget_data == NULL)
753ec753 1108 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
267b6db5 1109 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
753ec753 1110 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
267b6db5 1111
50a14534 1112 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
a0c65899 1113 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
50a14534 1114
c9570f86
RN
1115 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1116 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1117
1118 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1119 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1120 // and with normal PC processing capabilities - it has negligibile performance impact
1121 // This also minimized the amount of rework - as the management of the hash tables already exists.
1122
1123 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1124 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1125 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1126
1127 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1128 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
ce4bd1cf
RN
1129 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1130 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
0d2b891f
RN
1131 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1132 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
50a14534 1133
b1453c16 1134 // Default values
0d2b891f 1135 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
50a14534
EB
1136 rv->drawmode = drawmode;
1137 rv->drawpoints = TRUE;
3a1c8e42
RN
1138 rv->drawpoints_size = MIN_POINT_SIZE;
1139 rv->drawdirections_size = 5;
b42a25ba
EB
1140 rv->elevation_factor = 30;
1141 rv->stop_length = 60;
50a14534 1142 rv->drawlines = TRUE;
18df6d42 1143 rv->track_draw_speed_factor = 30.0;
50a14534 1144 rv->line_thickness = 1;
1eef1bde 1145
745fda83 1146 rv->draw_sync_done = TRUE;
ef5e8132 1147 rv->draw_sync_do = TRUE;
7b203521 1148
50a14534
EB
1149 rv->image_cache = g_queue_new();
1150 rv->image_size = 64;
1151 rv->image_alpha = 255;
1152 rv->image_cache_size = 300;
1153 rv->drawimages = TRUE;
1154 rv->drawlabels = TRUE;
b1453c16
RN
1155 // Everything else is 0, FALSE or NULL
1156
50a14534
EB
1157 return rv;
1158}
1159
1160
a7cd93ac 1161static void trw_layer_free ( VikTrwLayer *trwlayer )
50a14534
EB
1162{
1163 g_hash_table_destroy(trwlayer->waypoints);
1164 g_hash_table_destroy(trwlayer->tracks);
1165
1166 /* ODC: replace with GArray */
1167 trw_layer_free_track_gcs ( trwlayer );
1168
1169 if ( trwlayer->wp_right_click_menu )
4f14a010 1170 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
50a14534 1171
8bd81489
RN
1172 if ( trwlayer->track_right_click_menu )
1173 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1174
50a14534
EB
1175 if ( trwlayer->wplabellayout != NULL)
1176 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1177
1178 if ( trwlayer->waypoint_gc != NULL )
1179 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1180
1181 if ( trwlayer->waypoint_text_gc != NULL )
1182 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1183
1184 if ( trwlayer->waypoint_bg_gc != NULL )
1185 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1186
50a14534
EB
1187 if ( trwlayer->tpwin != NULL )
1188 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1189
1190 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1191 g_queue_free ( trwlayer->image_cache );
1192}
1193
08084371 1194static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
50a14534 1195{
08084371 1196 dp->vtl = vtl;
50a14534
EB
1197 dp->vp = vp;
1198 dp->xmpp = vik_viewport_get_xmpp ( vp );
1199 dp->ympp = vik_viewport_get_ympp ( vp );
1200 dp->width = vik_viewport_get_width ( vp );
1201 dp->height = vik_viewport_get_height ( vp );
3a1c8e42
RN
1202 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1203 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
08084371 1204
50a14534
EB
1205 dp->center = vik_viewport_get_center ( vp );
1206 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1207 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1208
1209 if ( dp->one_zone )
1210 {
1211 gint w2, h2;
1212 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1213 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1214 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1215
1216 dp->ce1 = dp->center->east_west-w2;
1217 dp->ce2 = dp->center->east_west+w2;
1218 dp->cn1 = dp->center->north_south-h2;
1219 dp->cn2 = dp->center->north_south+h2;
1220 } else if ( dp->lat_lon ) {
1221 VikCoord upperleft, bottomright;
1222 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1223 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1224 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1225 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1226 dp->ce1 = upperleft.east_west;
1227 dp->ce2 = bottomright.east_west;
1228 dp->cn1 = bottomright.north_south;
1229 dp->cn2 = upperleft.north_south;
1230 }
50a14534
EB
1231}
1232
18df6d42
RN
1233/*
1234 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1235 * Here a simple traffic like light colour system is used:
1236 * . slow points are red
1237 * . average is yellow
1238 * . fast points are green
1239 */
1240static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
50a14534 1241{
18df6d42
RN
1242 gdouble rv = 0;
1243 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1244 if ( average_speed > 0 ) {
1245 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1246 if ( rv < low_speed )
1247 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1248 else if ( rv > high_speed )
1249 return VIK_TRW_LAYER_TRACK_GC_FAST;
1250 else
1251 return VIK_TRW_LAYER_TRACK_GC_AVER;
1252 }
1253 }
1254 return VIK_TRW_LAYER_TRACK_GC_BLACK;
50a14534
EB
1255}
1256
5b309036 1257static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
50a14534
EB
1258{
1259 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1260 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1261 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1262 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1263}
1264
0c9b1cf7 1265static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
50a14534
EB
1266{
1267 /* TODO: this function is a mess, get rid of any redundancy */
1268 GList *list = track->trackpoints;
8e9c992d 1269 GdkGC *main_gc;
50a14534
EB
1270 gboolean useoldvals = TRUE;
1271
1272 gboolean drawpoints;
b42a25ba
EB
1273 gboolean drawstops;
1274 gboolean drawelevation;
941aa6e9 1275 gdouble min_alt, max_alt, alt_diff = 0;
50a14534 1276
3a1c8e42
RN
1277 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1278 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
50a14534
EB
1279 guint8 tp_size;
1280
b42a25ba
EB
1281 if ( dp->vtl->drawelevation )
1282 {
1283 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1284 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1285 alt_diff = max_alt - min_alt;
1286 }
1287
50a14534
EB
1288 if ( ! track->visible )
1289 return;
1290
1291 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
0c9b1cf7 1292 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
50a14534
EB
1293 trw_layer_draw_track ( name, track, dp, TRUE );
1294
0c9b1cf7 1295 if ( draw_track_outline )
b42a25ba
EB
1296 drawpoints = drawstops = FALSE;
1297 else {
50a14534 1298 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
1299 drawstops = dp->vtl->drawstops;
1300 }
50a14534 1301
18df6d42 1302 gboolean drawing_highlight = FALSE;
a5dcfdb7 1303 /* Current track - used for creation */
8e9c992d
EB
1304 if ( track == dp->vtl->current_track )
1305 main_gc = dp->vtl->current_track_gc;
a5dcfdb7
RN
1306 else {
1307 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1308 /* Draw all tracks of the layer in special colour */
1309 /* if track is member of selected layer or is the current selected track
1310 then draw in the highlight colour.
1311 NB this supercedes the drawmode */
1312 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
0d2b891f
RN
1313 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1314 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
a5dcfdb7 1315 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
480fb7e1 1316 main_gc = vik_viewport_get_gc_highlight (dp->vp);
18df6d42 1317 drawing_highlight = TRUE;
a5dcfdb7 1318 }
a5dcfdb7 1319 }
b1453c16
RN
1320 if ( !drawing_highlight ) {
1321 // Still need to figure out the gc according to the drawing mode:
1322 switch ( dp->vtl->drawmode ) {
1323 case DRAWMODE_BY_TRACK:
1324 if ( dp->vtl->track_1color_gc )
1325 g_object_unref ( dp->vtl->track_1color_gc );
1326 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1327 main_gc = dp->vtl->track_1color_gc;
1328 break;
1329 default:
1330 // Mostly for DRAWMODE_ALL_BLACK
1331 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1332 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_BLACK);
1333 break;
1334 }
a5dcfdb7
RN
1335 }
1336 }
8e9c992d 1337
50a14534
EB
1338 if (list) {
1339 int x, y, oldx, oldy;
1340 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1341
1342 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1343
1344 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1345
b1453c16
RN
1346 // Draw the first point as something a bit different from the normal points
1347 // ATM it's slightly bigger and a triangle
1348 if ( drawpoints ) {
50a14534 1349 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 1350 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
50a14534
EB
1351 }
1352
1353 oldx = x;
1354 oldy = y;
1355
18df6d42
RN
1356 gdouble average_speed = 0.0;
1357 gdouble low_speed = 0.0;
1358 gdouble high_speed = 0.0;
1359 // If necessary calculate these values - which is done only once per track redraw
1360 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1361 // the percentage factor away from the average speed determines transistions between the levels
1362 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1363 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1364 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1365 }
1366
50a14534
EB
1367 while ((list = g_list_next(list)))
1368 {
1369 tp = VIK_TRACKPOINT(list->data);
1370 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1371
1372 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1373 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1374 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1375 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1376 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1377 {
1378 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1379
adc271a8
GT
1380 /*
1381 * If points are the same in display coordinates, don't draw.
1382 */
1383 if ( useoldvals && x == oldx && y == oldy )
b827c5e0
RN
1384 {
1385 // Still need to process points to ensure 'stops' are drawn if required
0c9b1cf7 1386 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
b827c5e0
RN
1387 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1388 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 );
1389
adc271a8 1390 goto skip;
b827c5e0 1391 }
adc271a8 1392
18df6d42
RN
1393 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1394 if ( drawpoints || dp->vtl->drawlines ) {
1395 // setup main_gc for both point and line drawing
1396 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
b1453c16 1397 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
18df6d42
RN
1398 }
1399 }
1400
0c9b1cf7 1401 if ( drawpoints && ! draw_track_outline )
50a14534 1402 {
18df6d42 1403
50a14534 1404 if ( list->next ) {
4511d71f 1405 /*
f7eaf554 1406 * The concept of drawing stops is that a trackpoint
4511d71f
GT
1407 * that is if the next trackpoint has a timestamp far into
1408 * the future, we draw a circle of 6x trackpoint size,
f7eaf554
RN
1409 * instead of a rectangle of 2x trackpoint size.
1410 * This is drawn first so the trackpoint will be drawn on top
4511d71f 1411 */
50a14534 1412 /* stops */
b42a25ba 1413 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
b1453c16
RN
1414 /* Stop point. Draw 6x circle. Always in redish colour */
1415 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
f7eaf554
RN
1416
1417 /* Regular point - draw 2x square. */
1418 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
50a14534
EB
1419 }
1420 else
4511d71f 1421 /* Final point - draw 4x circle. */
8e9c992d 1422 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
1423 }
1424
1425 if ((!tp->newsegment) && (dp->vtl->drawlines))
1426 {
50a14534
EB
1427
1428 /* UTM only: zone check */
1429 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
8e9c992d 1430 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
50a14534 1431
50a14534
EB
1432 if (!useoldvals)
1433 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1434
0c9b1cf7 1435 if ( draw_track_outline ) {
50a14534 1436 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
1437 }
1438 else {
8c4f1350 1439
8e9c992d 1440 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
08084371 1441
3b9970f8 1442 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
b42a25ba
EB
1443 GdkPoint tmp[4];
1444 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
3b9970f8
RN
1445
1446 tmp[0].x = oldx;
1447 tmp[0].y = oldy;
1448 tmp[1].x = oldx;
1449 tmp[1].y = oldy-FIXALTITUDE(list->data);
1450 tmp[2].x = x;
1451 tmp[2].y = y-FIXALTITUDE(list->next->data);
1452 tmp[3].x = x;
1453 tmp[3].y = y;
1454
1455 GdkGC *tmp_gc;
1456 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1457 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1458 else
1459 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1460 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1461
8e9c992d 1462 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
8c4f1350
EB
1463 }
1464 }
50a14534
EB
1465 }
1466
08084371
RN
1467 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1468 // Draw an arrow at the mid point to show the direction of the track
1469 // Code is a rework from vikwindow::draw_ruler()
1470 gint midx = (oldx + x) / 2;
1471 gint midy = (oldy + y) / 2;
1472
1473 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1474 // Avoid divide by zero and ensure at least 1 pixel big
1475 if ( len > 1 ) {
1476 gdouble dx = (oldx - midx) / len;
1477 gdouble dy = (oldy - midy) / len;
1478 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1479 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1480 }
1481 }
1482
adc271a8 1483 skip:
50a14534
EB
1484 oldx = x;
1485 oldy = y;
1486 useoldvals = TRUE;
1487 }
1488 else {
1489 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1490 {
1491 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1492 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1493 {
1494 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
18df6d42
RN
1495
1496 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
b1453c16 1497 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
18df6d42 1498 }
50a14534 1499
adc271a8
GT
1500 /*
1501 * If points are the same in display coordinates, don't draw.
1502 */
1503 if ( x != oldx || y != oldy )
1504 {
0c9b1cf7 1505 if ( draw_track_outline )
adc271a8
GT
1506 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1507 else
1508 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1509 }
50a14534
EB
1510 }
1511 else
1512 {
adc271a8
GT
1513 /*
1514 * If points are the same in display coordinates, don't draw.
1515 */
1516 if ( x != oldx && y != oldy )
1517 {
1518 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1519 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1520 }
50a14534
EB
1521 }
1522 }
1523 useoldvals = FALSE;
1524 }
1525 }
1526 }
50a14534
EB
1527}
1528
1529/* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1530static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1531{
1532 trw_layer_draw_track ( name, track, dp, FALSE );
1533}
1534
1535static void cached_pixbuf_free ( CachedPixbuf *cp )
1536{
1537 g_object_unref ( G_OBJECT(cp->pixbuf) );
1538 g_free ( cp->image );
1539}
1540
1541static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1542{
1543 return strcmp ( cp->image, name );
1544}
1545
1546static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1547{
1548 if ( wp->visible )
51f0884d 1549 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
50a14534
EB
1550 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1551 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1552 {
1553 gint x, y;
a7f9c01e 1554 GdkPixbuf *sym = NULL;
50a14534
EB
1555 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1556
1557 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1558
1559 if ( wp->image && dp->vtl->drawimages )
1560 {
1561 GdkPixbuf *pixbuf = NULL;
1562 GList *l;
1563
1564 if ( dp->vtl->image_alpha == 0)
1565 return;
1566
1567 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1568 if ( l )
1569 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1570 else
1571 {
1572 gchar *image = wp->image;
1573 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1574 if ( ! regularthumb )
1575 {
1576 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1577 image = "\x12\x00"; /* this shouldn't occur naturally. */
1578 }
1579 if ( regularthumb )
1580 {
1581 CachedPixbuf *cp = NULL;
1582 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1583 if ( dp->vtl->image_size == 128 )
1584 cp->pixbuf = regularthumb;
1585 else
1586 {
1587 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1588 g_assert ( cp->pixbuf );
1589 g_object_unref ( G_OBJECT(regularthumb) );
1590 }
1591 cp->image = g_strdup ( image );
1592
1593 /* needed so 'click picture' tool knows how big the pic is; we don't
1594 * store it in cp because they may have been freed already. */
1595 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1596 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1597
1598 g_queue_push_head ( dp->vtl->image_cache, cp );
1599 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1600 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1601
1602 pixbuf = cp->pixbuf;
1603 }
1604 else
1605 {
1606 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1607 }
1608 }
1609 if ( pixbuf )
1610 {
1611 gint w, h;
1612 w = gdk_pixbuf_get_width ( pixbuf );
1613 h = gdk_pixbuf_get_height ( pixbuf );
1614
1615 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1616 {
a5dcfdb7
RN
1617 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1618 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1619 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1620 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1621 // Highlighted - so draw a little border around the chosen one
1622 // single line seems a little weak so draw 2 of them
480fb7e1 1623 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
a5dcfdb7 1624 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
480fb7e1 1625 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
a5dcfdb7
RN
1626 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1627 }
1628 }
50a14534
EB
1629 if ( dp->vtl->image_alpha == 255 )
1630 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1631 else
1632 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1633 }
1634 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1635 }
1636 }
1637
1638 /* DRAW ACTUAL DOT */
ea3933fc 1639 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
acaf7113
AF
1640 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 );
1641 }
1642 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
1643 switch ( dp->vtl->wp_symbol ) {
1644 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;
1645 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;
1646 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;
1647 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 );
1648 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 );
1649 }
1650 }
1651 else {
1652 switch ( dp->vtl->wp_symbol ) {
1653 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;
1654 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;
1655 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;
1656 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 );
1657 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;
1658 }
1659 }
1660
1661 if ( dp->vtl->drawlabels )
1662 {
1663 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
a7f9c01e 1664 gint label_x, label_y;
50a14534 1665 gint width, height;
5a77ae71
RN
1666 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1667
1668 // Could this stored in the waypoint rather than recreating each pass?
1669 gchar *fsize = NULL;
1670 switch (dp->vtl->wp_font_size) {
1671 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1672 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1673 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1674 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1675 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1676 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1677 default: fsize = g_strdup ( "medium" ); break;
1678 }
1679
1680 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1681
1682 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1683 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1684 else
1685 // Fallback if parse failure
1686 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1687
1688 g_free ( wp_label_markup );
1689 g_free ( fsize );
1690
50a14534 1691 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
a7f9c01e
QT
1692 label_x = x - width/2;
1693 if (sym)
1694 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1695 else
1696 label_y = y - dp->vtl->wp_size - height - 2;
1697
480fb7e1 1698 /* if highlight mode on, then draw background text in highlight colour */
a5dcfdb7
RN
1699 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1700 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1701 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1702 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
480fb7e1 1703 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
1704 else
1705 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1706 }
1707 else {
1708 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1709 }
a7f9c01e 1710 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
50a14534
EB
1711 }
1712 }
1713}
1714
a7cd93ac 1715static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
50a14534
EB
1716{
1717 static struct DrawingParams dp;
1718 g_assert ( l != NULL );
1719
08084371 1720 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
50a14534
EB
1721
1722 if ( l->tracks_visible )
1723 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1724
0d2b891f
RN
1725 if ( l->routes_visible )
1726 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1727
50a14534
EB
1728 if (l->waypoints_visible)
1729 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1730}
1731
1732static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1733{
1734 int i;
1735 if ( vtl->track_bg_gc )
1736 {
1737 g_object_unref ( vtl->track_bg_gc );
1738 vtl->track_bg_gc = NULL;
1739 }
b1453c16
RN
1740 if ( vtl->track_1color_gc )
1741 {
1742 g_object_unref ( vtl->track_1color_gc );
1743 vtl->track_1color_gc = NULL;
1744 }
8e9c992d
EB
1745 if ( vtl->current_track_gc )
1746 {
1747 g_object_unref ( vtl->current_track_gc );
1748 vtl->current_track_gc = NULL;
1749 }
745fda83
RN
1750 if ( vtl->current_track_newpoint_gc )
1751 {
1752 g_object_unref ( vtl->current_track_newpoint_gc );
1753 vtl->current_track_newpoint_gc = NULL;
1754 }
50a14534
EB
1755
1756 if ( ! vtl->track_gc )
1757 return;
1758 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1759 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1760 g_array_free ( vtl->track_gc, TRUE );
1761 vtl->track_gc = NULL;
1762}
1763
1764static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1765{
1766 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1767 gint width = vtl->line_thickness;
1768
1769 if ( vtl->track_gc )
1770 trw_layer_free_track_gcs ( vtl );
1771
1772 if ( vtl->track_bg_gc )
1773 g_object_unref ( vtl->track_bg_gc );
1774 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1775
98b7eddc
RN
1776 // Ensure new track drawing heeds line thickness setting
1777 // however always have a minium of 2, as 1 pixel is really narrow
1778 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1779
8e9c992d
EB
1780 if ( vtl->current_track_gc )
1781 g_object_unref ( vtl->current_track_gc );
98b7eddc
RN
1782 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1783 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
8e9c992d 1784
745fda83
RN
1785 // 'newpoint' gc is exactly the same as the current track gc
1786 if ( vtl->current_track_newpoint_gc )
1787 g_object_unref ( vtl->current_track_newpoint_gc );
98b7eddc
RN
1788 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1789 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
745fda83 1790
50a14534
EB
1791 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1792
b1453c16
RN
1793 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1794 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
50a14534 1795
18df6d42
RN
1796 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1797 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1798 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1799
50a14534
EB
1800 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1801}
1802
a7cd93ac 1803static VikTrwLayer* trw_layer_create ( VikViewport *vp )
50a14534 1804{
a7cd93ac 1805 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
ba8be43f
RN
1806 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1807
1808 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1809 /* early exit, as the rest is GUI related */
1810 return rv;
1811 }
1812
50a14534 1813 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
56cb1807 1814 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
50a14534 1815
50a14534
EB
1816 trw_layer_new_track_gcs ( rv, vp );
1817
1818 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1819 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1820 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1821 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1822
50a14534
EB
1823 rv->has_verified_thumbnails = FALSE;
1824 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1825 rv->wp_size = 4;
ea3933fc 1826 rv->wp_draw_symbols = TRUE;
56cb1807 1827 rv->wp_font_size = FS_MEDIUM;
50a14534
EB
1828
1829 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1830
20c7a3a0
QT
1831 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1832
50a14534
EB
1833 return rv;
1834}
1835
70cefedc
RN
1836#define SMALL_ICON_SIZE 18
1837/*
1838 * Can accept a null symbol, and may return null value
1839 */
1840static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1841{
1842 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1843 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1844 // So needing a small icon for the treeview may need some resizing:
1845 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1846 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1847 return wp_icon;
1848}
1849
ce4bd1cf 1850static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
50a14534
EB
1851{
1852 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1853
b1453c16
RN
1854 GdkPixbuf *pixbuf = NULL;
1855
1856 if ( track->has_color ) {
1857 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1858 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1859 // Here is some magic found to do the conversion
1860 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1861 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1862 ((track->color.green & 0xff00) << 8) |
1863 (track->color.blue & 0xff00);
1864
1865 gdk_pixbuf_fill ( pixbuf, pixel );
1866 }
1867
50a14534 1868#ifdef VIK_CONFIG_ALPHABETIZED_TRW
b1453c16 1869 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]), pixbuf, TRUE, TRUE );
50a14534 1870#else
b1453c16 1871 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]), pixbuf, TRUE, TRUE );
50a14534
EB
1872#endif
1873
b1453c16
RN
1874 if ( pixbuf )
1875 g_object_unref (pixbuf);
1876
50a14534 1877 *new_iter = *((GtkTreeIter *) pass_along[1]);
0d2b891f
RN
1878 if ( track->is_route )
1879 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1880 else
1881 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
50a14534
EB
1882
1883 if ( ! track->visible )
1884 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1885}
1886
c9570f86 1887static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
50a14534
EB
1888{
1889 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
70cefedc 1890
50a14534 1891#ifdef VIK_CONFIG_ALPHABETIZED_TRW
70cefedc 1892 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]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
50a14534 1893#else
70cefedc 1894 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]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
50a14534
EB
1895#endif
1896
1897 *new_iter = *((GtkTreeIter *) pass_along[1]);
c9570f86 1898 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
50a14534
EB
1899
1900 if ( ! wp->visible )
1901 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1902}
1903
cd3f311e 1904static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
50a14534 1905{
50a14534 1906#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1907 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534 1908#else
4c77d5e0 1909 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534 1910#endif
cd3f311e 1911}
50a14534 1912
cd3f311e
RN
1913static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1914{
50a14534 1915#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1916 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534 1917#else
4c77d5e0 1918 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534 1919#endif
cd3f311e 1920}
50a14534 1921
0d2b891f
RN
1922static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1923{
1924#ifdef VIK_CONFIG_ALPHABETIZED_TRW
1925 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1926#else
1927 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1928#endif
1929}
1930
cd3f311e
RN
1931static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1932{
1933 GtkTreeIter iter2;
1934 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
50a14534 1935
cd3f311e
RN
1936 if ( g_hash_table_size (vtl->tracks) > 0 ) {
1937 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1938 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
50a14534 1939
cd3f311e
RN
1940 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1941 }
50a14534 1942
0d2b891f
RN
1943 if ( g_hash_table_size (vtl->routes) > 0 ) {
1944
1945 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1946
1947 pass_along[0] = &(vtl->routes_iter);
1948 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1949
1950 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1951
1952 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1953 }
1954
cd3f311e 1955 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
0d2b891f 1956 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
cd3f311e
RN
1957
1958 pass_along[0] = &(vtl->waypoints_iter);
1959 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1960
1961 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1962
1963 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1964 }
0d2b891f 1965
50a14534
EB
1966}
1967
a7cd93ac 1968static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
50a14534
EB
1969{
1970 switch ( subtype )
1971 {
1972 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1973 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
0d2b891f 1974 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
50a14534
EB
1975 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1976 {
1977 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1978 if (t)
1979 return (t->visible ^= 1);
1980 else
1981 return TRUE;
1982 }
1983 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1984 {
1985 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1986 if (t)
1987 return (t->visible ^= 1);
1988 else
1989 return TRUE;
1990 }
0d2b891f
RN
1991 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
1992 {
1993 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
1994 if (t)
1995 return (t->visible ^= 1);
1996 else
1997 return TRUE;
1998 }
50a14534
EB
1999 }
2000 return TRUE;
2001}
2002
04f36d92
RN
2003/*
2004 * Return a property about tracks for this layer
2005 */
2006gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2007{
2008 return vtl->line_thickness;
2009}
2010
cb89c5a5
RN
2011// Structure to hold multiple track information for a layer
2012typedef struct {
2013 gdouble length;
2014 time_t start_time;
2015 time_t end_time;
2016 gint duration;
2017} tooltip_tracks;
2018
2019/*
2020 * Build up layer multiple track information via updating the tooltip_tracks structure
2021 */
2022static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2023{
2024 tt->length = tt->length + vik_track_get_length (tr);
2025
2026 // Ensure times are available
2027 if ( tr->trackpoints &&
2028 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2029 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2030
2031 time_t t1, t2;
2032 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2033 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2034
2035 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2036 // Hence initialize to the first 'proper' value
2037 if ( tt->start_time == 0 )
2038 tt->start_time = t1;
2039 if ( tt->end_time == 0 )
2040 tt->end_time = t2;
2041
2042 // Update find the earliest / last times
2043 if ( t1 < tt->start_time )
2044 tt->start_time = t1;
2045 if ( t2 > tt->end_time )
2046 tt->end_time = t2;
2047
2048 // Keep track of total time
2049 // there maybe gaps within a track (eg segments)
2050 // but this should be generally good enough for a simple indicator
2051 tt->duration = tt->duration + (int)(t2-t1);
2052 }
2053}
2054
2055/*
2056 * Generate tooltip text for the layer.
2057 * This is relatively complicated as it considers information for
2058 * no tracks, a single track or multiple tracks
2059 * (which may or may not have timing information)
2060 */
2061static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2062{
2063 gchar tbuf1[32];
2064 gchar tbuf2[64];
2065 gchar tbuf3[64];
2066 gchar tbuf4[10];
2067 tbuf1[0] = '\0';
2068 tbuf2[0] = '\0';
2069 tbuf3[0] = '\0';
2070 tbuf4[0] = '\0';
2071
2072 static gchar tmp_buf[128];
2073 tmp_buf[0] = '\0';
2074
2075 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2076
2077 // Safety check - I think these should always be valid
2078 if ( vtl->tracks && vtl->waypoints ) {
2079 tooltip_tracks tt = { 0.0, 0, 0 };
2080 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2081
2082 GDate* gdate_start = g_date_new ();
2083 g_date_set_time_t (gdate_start, tt.start_time);
2084
2085 GDate* gdate_end = g_date_new ();
2086 g_date_set_time_t (gdate_end, tt.end_time);
2087
2088 if ( g_date_compare (gdate_start, gdate_end) ) {
2089 // Dates differ so print range on separate line
2090 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2091 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2092 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2093 }
2094 else {
2095 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2096 if ( tt.start_time != 0 )
2097 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2098 }
2099
2100 tbuf2[0] = '\0';
2101 if ( tt.length > 0.0 ) {
2102 gdouble len_in_units;
2103
2104 // Setup info dependent on distance units
2105 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2106 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
a492ff13 2107 len_in_units = VIK_METERS_TO_MILES(tt.length);
cb89c5a5
RN
2108 }
2109 else {
2110 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2111 len_in_units = tt.length/1000.0;
2112 }
2113
2114 // Timing information if available
2115 tbuf1[0] = '\0';
2116 if ( tt.duration > 0 ) {
2117 g_snprintf (tbuf1, sizeof(tbuf1),
2118 _(" in %d:%02d hrs:mins"),
2119 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2120 }
2121 g_snprintf (tbuf2, sizeof(tbuf2),
2122 _("\n%sTotal Length %.1f %s%s"),
2123 tbuf3, len_in_units, tbuf4, tbuf1);
2124 }
2125
2126 // Put together all the elements to form compact tooltip text
2127 g_snprintf (tmp_buf, sizeof(tmp_buf),
0d2b891f
RN
2128 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2129 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
cb89c5a5
RN
2130
2131 g_date_free (gdate_start);
2132 g_date_free (gdate_end);
2133
2134 }
2135
2136 return tmp_buf;
2137}
2138
c7060c4e
RN
2139static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2140{
2141 switch ( subtype )
2142 {
cc837b7a
RN
2143 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2144 {
2145 // Very simple tooltip - may expand detail in the future...
2146 static gchar tmp_buf[32];
2147 g_snprintf (tmp_buf, sizeof(tmp_buf),
2148 _("Tracks: %d"),
2149 g_hash_table_size (l->tracks));
2150 return tmp_buf;
2151 }
2152 break;
0d2b891f
RN
2153 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2154 {
2155 // Very simple tooltip - may expand detail in the future...
2156 static gchar tmp_buf[32];
2157 g_snprintf (tmp_buf, sizeof(tmp_buf),
2158 _("Routes: %d"),
2159 g_hash_table_size (l->routes));
2160 return tmp_buf;
2161 }
2162 break;
2163
2164 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2165 // Same tooltip for a route
c7060c4e
RN
2166 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2167 {
0d2b891f
RN
2168 VikTrack *tr;
2169 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2170 tr = g_hash_table_lookup ( l->tracks, sublayer );
2171 else
2172 tr = g_hash_table_lookup ( l->routes, sublayer );
2173
c7060c4e
RN
2174 if ( tr ) {
2175 // Could be a better way of handling strings - but this works...
2176 gchar time_buf1[20];
2177 gchar time_buf2[20];
2178 time_buf1[0] = '\0';
2179 time_buf2[0] = '\0';
2180 static gchar tmp_buf[100];
2181 // Compact info: Short date eg (11/20/99), duration and length
2182 // Hopefully these are the things that are most useful and so promoted into the tooltip
2183 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2184 // %x The preferred date representation for the current locale without the time.
2185 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2186 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2187 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2188 if ( dur > 0 )
cb89c5a5 2189 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
c7060c4e
RN
2190 }
2191 }
2192 // Get length and consider the appropriate distance units
2193 gdouble tr_len = vik_track_get_length(tr);
2194 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2195 switch (dist_units) {
2196 case VIK_UNITS_DISTANCE_KILOMETRES:
2197 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2198 break;
2199 case VIK_UNITS_DISTANCE_MILES:
a492ff13 2200 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
c7060c4e
RN
2201 break;
2202 default:
2203 break;
2204 }
2205 return tmp_buf;
2206 }
2207 }
2208 break;
cc837b7a
RN
2209 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2210 {
2211 // Very simple tooltip - may expand detail in the future...
2212 static gchar tmp_buf[32];
2213 g_snprintf (tmp_buf, sizeof(tmp_buf),
2214 _("Waypoints: %d"),
2215 g_hash_table_size (l->waypoints));
2216 return tmp_buf;
2217 }
2218 break;
c7060c4e
RN
2219 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2220 {
2221 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2222 // NB It's OK to return NULL
c9570f86
RN
2223 if ( w )
2224 return w->comment;
c7060c4e
RN
2225 }
2226 break;
2227 default: break;
2228 }
2229 return NULL;
2230}
2231
95d1b757
RN
2232/*
2233 * Function to show basic track point information on the statusbar
2234 */
2235static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2236{
2237 gchar tmp_buf1[64];
2238 switch (a_vik_get_units_height ()) {
2239 case VIK_UNITS_HEIGHT_FEET:
2240 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2241 break;
2242 default:
2243 //VIK_UNITS_HEIGHT_METRES:
2244 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2245 }
2246
2247 gchar tmp_buf2[64];
2248 tmp_buf2[0] = '\0';
2249 if ( trkpt->has_timestamp ) {
2250 // Compact date time format
2251 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2252 }
2253
2254 // Position part
2255 // Position is put later on, as this bit may not be seen if the display is not big enough,
2256 // one can easily use the current pointer position to see this if needed
2257 gchar *lat = NULL, *lon = NULL;
2258 static struct LatLon ll;
2259 vik_coord_to_latlon (&(trkpt->coord), &ll);
2260 a_coords_latlon_to_string ( &ll, &lat, &lon );
2261
2262 // Track name
2263 // Again is put later on, as this bit may not be seen if the display is not big enough
2264 // trackname can be seen from the treeview (when enabled)
2265 // Also name could be very long to not leave room for anything else
2266 gchar tmp_buf3[64];
2267 tmp_buf3[0] = '\0';
ce4bd1cf
RN
2268 if ( vtl->current_tp_track ) {
2269 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
95d1b757
RN
2270 }
2271
2272 // Combine parts to make overall message
2273 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2274 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2275 g_free ( lat );
2276 g_free ( lon );
2277 g_free ( msg );
2278}
2279
2280/*
2281 * Function to show basic waypoint information on the statusbar
2282 */
2283static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2284{
2285 gchar tmp_buf1[64];
2286 switch (a_vik_get_units_height ()) {
2287 case VIK_UNITS_HEIGHT_FEET:
2288 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2289 break;
2290 default:
2291 //VIK_UNITS_HEIGHT_METRES:
2292 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2293 }
2294
2295 // Position part
2296 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2297 // one can easily use the current pointer position to see this if needed
2298 gchar *lat = NULL, *lon = NULL;
2299 static struct LatLon ll;
2300 vik_coord_to_latlon (&(wpt->coord), &ll);
2301 a_coords_latlon_to_string ( &ll, &lat, &lon );
2302
2303 // Combine parts to make overall message
2304 gchar *msg;
2305 if ( wpt->comment )
2306 // Add comment if available
2307 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2308 else
2309 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2310 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2311 g_free ( lat );
2312 g_free ( lon );
2313 g_free ( msg );
2314}
2315
a5dcfdb7
RN
2316/**
2317 * General layer selection function, find out which bit is selected and take appropriate action
2318 */
a7cd93ac 2319static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
a5dcfdb7 2320{
c95d6b00 2321 // Reset
c9570f86
RN
2322 l->current_wp = NULL;
2323 l->current_wp_id = NULL;
c95d6b00
RN
2324 trw_layer_cancel_current_tp ( l, FALSE );
2325
95d1b757
RN
2326 // Clear statusbar
2327 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2328
a5dcfdb7
RN
2329 switch ( type )
2330 {
2331 case VIK_TREEVIEW_TYPE_LAYER:
2332 {
2333 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2334 /* Mark for redraw */
2335 return TRUE;
2336 }
2337 break;
2338
2339 case VIK_TREEVIEW_TYPE_SUBLAYER:
2340 {
2341 switch ( subtype )
2342 {
2343 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2344 {
113c74f6 2345 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
a5dcfdb7
RN
2346 /* Mark for redraw */
2347 return TRUE;
2348 }
2349 break;
2350 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2351 {
95d1b757 2352 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
b16effab 2353 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
a5dcfdb7
RN
2354 /* Mark for redraw */
2355 return TRUE;
2356 }
2357 break;
0d2b891f
RN
2358 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2359 {
2360 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2361 /* Mark for redraw */
2362 return TRUE;
2363 }
2364 break;
2365 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2366 {
2367 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2368 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2369 /* Mark for redraw */
2370 return TRUE;
2371 }
2372 break;
a5dcfdb7
RN
2373 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2374 {
113c74f6 2375 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
a5dcfdb7
RN
2376 /* Mark for redraw */
2377 return TRUE;
2378 }
2379 break;
2380 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2381 {
b16effab
RN
2382 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2383 if ( wpt ) {
2384 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2385 // Show some waypoint info
2386 set_statusbar_msg_info_wpt ( l, wpt );
2387 /* Mark for redraw */
2388 return TRUE;
2389 }
a5dcfdb7
RN
2390 }
2391 break;
2392 default:
2393 {
2394 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2395 }
2396 break;
2397 }
2398 return FALSE;
2399 }
2400 break;
2401
2402 default:
2403 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2404 break;
2405 }
2406}
c7060c4e 2407
50a14534
EB
2408GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2409{
2410 return l->tracks;
2411}
2412
0d2b891f
RN
2413GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2414{
2415 return l->routes;
2416}
2417
50a14534
EB
2418GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2419{
2420 return l->waypoints;
2421}
2422
c9570f86
RN
2423/*
2424 * ATM use a case sensitive find
2425 * Finds the first one
2426 */
2427static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2428{
2429 if ( wp && wp->name )
2430 if ( ! strcmp ( wp->name, name ) )
2431 return TRUE;
2432 return FALSE;
2433}
2434
2435/*
2436 * Get waypoint by name - not guaranteed to be unique
2437 * Finds the first one
2438 */
2439VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2440{
2441 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2442}
2443
ce4bd1cf
RN
2444/*
2445 * ATM use a case sensitive find
2446 * Finds the first one
2447 */
2448static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2449{
2450 if ( trk && trk->name )
2451 if ( ! strcmp ( trk->name, name ) )
2452 return TRUE;
2453 return FALSE;
2454}
2455
2456/*
2457 * Get track by name - not guaranteed to be unique
2458 * Finds the first one
2459 */
c9570f86
RN
2460VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2461{
ce4bd1cf 2462 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
c9570f86
RN
2463}
2464
0d2b891f
RN
2465/*
2466 * Get route by name - not guaranteed to be unique
2467 * Finds the first one
2468 */
2469VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2470{
2471 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2472}
2473
50a14534
EB
2474static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2475{
2476 static VikCoord fixme;
2477 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2478 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2479 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2480 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2481 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2482 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2483 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2484 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2485 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2486}
2487
867f4673 2488static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
50a14534 2489{
867f4673 2490 GList *tr = trk->trackpoints;
50a14534
EB
2491 static VikCoord fixme;
2492
2493 while ( tr )
2494 {
2495 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2496 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2497 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2498 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2499 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2500 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2501 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2502 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2503 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2504 tr = tr->next;
2505 }
2506}
2507
165a4fa9
HR
2508static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2509{
0d2b891f
RN
2510 // Continually reuse maxmin to find the latest maximum and minimum values
2511 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2512 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2513 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
165a4fa9 2514}
50a14534
EB
2515
2516gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2517{
2518 /* 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... */
2519 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
165a4fa9 2520 trw_layer_find_maxmin (vtl, maxmin);
50a14534
EB
2521 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2522 return FALSE;
2523 else
2524 {
2525 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2526 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2527 return TRUE;
2528 }
2529}
2530
2531static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2532{
2533 VikCoord coord;
2534 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
6bb72350 2535 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
50a14534 2536 else
4c77d5e0 2537 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
2538}
2539
c5638216
RN
2540static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2541{
2542 /* First set the center [in case previously viewing from elsewhere] */
2543 /* Then loop through zoom levels until provided positions are in view */
2544 /* This method is not particularly fast - but should work well enough */
2545 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2546 VikCoord coord;
2547 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2548 vik_viewport_set_center_coord ( vvp, &coord );
2549
2550 /* Convert into definite 'smallest' and 'largest' positions */
2551 struct LatLon minmin;
2552 if ( maxmin[0].lat < maxmin[1].lat )
2553 minmin.lat = maxmin[0].lat;
2554 else
2555 minmin.lat = maxmin[1].lat;
2556
2557 struct LatLon maxmax;
2558 if ( maxmin[0].lon > maxmin[1].lon )
2559 maxmax.lon = maxmin[0].lon;
2560 else
2561 maxmax.lon = maxmin[1].lon;
2562
2563 /* Never zoom in too far - generally not that useful, as too close ! */
2564 /* Always recalculate the 'best' zoom level */
2565 gdouble zoom = 1.0;
2566 vik_viewport_set_zoom ( vvp, zoom );
2567
2568 gdouble min_lat, max_lat, min_lon, max_lon;
2569 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2570 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2571 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2572 /* NB I think the logic used in this test to determine if the bounds is within view
2573 fails if track goes across 180 degrees longitude.
2574 Hopefully that situation is not too common...
2575 Mind you viking doesn't really do edge locations to well anyway */
2576 if ( min_lat < minmin.lat &&
2577 max_lat > minmin.lat &&
2578 min_lon < maxmax.lon &&
2579 max_lon > maxmax.lon )
2580 /* Found within zoom level */
2581 break;
2582
2583 /* Try next */
2584 zoom = zoom * 2;
2585 vik_viewport_set_zoom ( vvp, zoom );
2586 }
2587}
2588
2589gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2590{
2591 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2592 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2593 trw_layer_find_maxmin (vtl, maxmin);
2594 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2595 return FALSE;
2596 else {
2597 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2598 return TRUE;
2599 }
2600}
2601
5a10c240
RN
2602static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2603{
2604 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])) ) ) {
2605 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2606 }
2607 else
2608 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2609}
2610
ce4bd1cf 2611static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
50a14534
EB
2612{
2613 GtkWidget *file_selector;
2614 const gchar *fn;
2615 gboolean failed = FALSE;
7f6757c4
RN
2616 file_selector = gtk_file_chooser_dialog_new (title,
2617 NULL,
2618 GTK_FILE_CHOOSER_ACTION_SAVE,
2619 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2620 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2621 NULL);
2622 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
6e4a49aa
MA
2623
2624 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
50a14534 2625 {
6e4a49aa 2626 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
45acf79e 2627 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
50a14534
EB
2628 {
2629 gtk_widget_hide ( file_selector );
208d2084 2630 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
50a14534
EB
2631 break;
2632 }
2633 else
2634 {
d91e5f2b 2635 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
2636 {
2637 gtk_widget_hide ( file_selector );
208d2084 2638 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
50a14534
EB
2639 break;
2640 }
2641 }
2642 }
2643 gtk_widget_destroy ( file_selector );
2644 if ( failed )
4c77d5e0 2645 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
2646}
2647
2648static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2649{
f7f8a0a6 2650 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
50a14534
EB
2651}
2652
2653static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2654{
f7f8a0a6 2655 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
50a14534
EB
2656}
2657
561e6ad0
EB
2658static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2659{
18d0a1ed
RN
2660 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2661 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2662 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2663 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2664
2665 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2666
2667 g_free ( auto_save_name );
7f6757c4
RN
2668}
2669
ba9d0a00
RN
2670static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2671{
2672 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2673 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2674 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2675 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2676
2677 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2678
2679 g_free ( auto_save_name );
2680}
2681
ccccf356
RN
2682/**
2683 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2684 *
2685 */
2686static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2687{
2688 gchar *name_used = NULL;
2689 int fd;
2690
2691 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
208d2084 2692 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
ccccf356
RN
2693 if (failed) {
2694 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2695 }
2696 else {
2697 GError *err = NULL;
2698 gchar *quoted_file = g_shell_quote ( name_used );
2699 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2700 g_free ( quoted_file );
2701 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2702 {
2703 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2704 g_error_free ( err );
2705 }
2706 g_free ( cmd );
2707 }
2708 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2709 //g_remove ( name_used );
2710 // Perhaps should be deleted when the program ends?
2711 // For now leave it to the user to delete it / use system temp cleanup methods.
2712 g_free ( name_used );
2713 }
2714}
2715
2716static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2717{
2718 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2719}
2720
ff02058b
RN
2721static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2722{
2723 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2724}
2725
7f6757c4
RN
2726static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2727{
7f6757c4
RN
2728 gpointer layer_and_vlp[2];
2729 layer_and_vlp[0] = pass_along[0];
2730 layer_and_vlp[1] = pass_along[1];
0d2b891f
RN
2731
2732 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2733 VikTrack *trk;
2734 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2735 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2736 else
2737 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
ce4bd1cf
RN
2738
2739 if ( !trk || !trk->name )
2740 return;
7f6757c4
RN
2741
2742 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
ce4bd1cf 2743 gchar *auto_save_name = g_strdup ( trk->name );
7f6757c4
RN
2744 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2745 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2746
ce4bd1cf 2747 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
7f6757c4
RN
2748
2749 g_free ( auto_save_name );
561e6ad0
EB
2750}
2751
c9570f86
RN
2752typedef struct {
2753 VikWaypoint *wp; // input
2754 gpointer uuid; // output
2755} wpu_udata;
2756
2757static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2758{
2759 wpu_udata *user_data = udata;
2760 if ( wp == user_data->wp ) {
2761 user_data->uuid = id;
2762 return TRUE;
2763 }
2764 return FALSE;
2765}
2766
50a14534
EB
2767static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2768{
a77d62d8 2769 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
50a14534
EB
2770 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2771 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2772 GTK_STOCK_CANCEL,
2773 GTK_RESPONSE_REJECT,
2774 GTK_STOCK_OK,
2775 GTK_RESPONSE_ACCEPT,
2776 NULL);
2777
2778 GtkWidget *label, *entry;
4c77d5e0 2779 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
2780 entry = gtk_entry_new();
2781
2782 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2783 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2784 gtk_widget_show_all ( label );
2785 gtk_widget_show_all ( entry );
2786
7ea3cb11
RN
2787 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2788
50a14534
EB
2789 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2790 {
c9570f86
RN
2791 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2792 // Find *first* wp with the given name
2793 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
50a14534 2794
c9570f86 2795 if ( !wp )
4c77d5e0 2796 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
50a14534
EB
2797 else
2798 {
2799 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2800 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
c9570f86
RN
2801
2802 // Find and select on the side panel
2803 wpu_udata udata;
2804 udata.wp = wp;
2805 udata.uuid = NULL;
2806
2807 // Hmmm, want key of it
2808 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2809
2810 if ( wpf && udata.uuid ) {
2811 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2812 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2813 }
2814
50a14534
EB
2815 break;
2816 }
2817
c9570f86 2818 g_free ( name );
50a14534
EB
2819
2820 }
2821 gtk_widget_destroy ( dia );
2822}
2823
2824gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2825{
ac1bde8b 2826 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 2827 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
2828 gchar *returned_name;
2829 gboolean updated;
acaf7113 2830 wp->coord = *def_coord;
d60a672e
RN
2831
2832 // Attempt to auto set height if DEM data is available
2833 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2834 if ( elev != VIK_DEM_INVALID_ELEVATION )
2835 wp->altitude = (gdouble)elev;
50a14534 2836
c9570f86
RN
2837 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2838
2839 if ( returned_name )
50a14534 2840 {
805d282e 2841 wp->visible = TRUE;
ac1bde8b
RN
2842 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2843 g_free (default_name);
c9570f86 2844 g_free (returned_name);
50a14534
EB
2845 return TRUE;
2846 }
ac1bde8b 2847 g_free (default_name);
acaf7113 2848 vik_waypoint_free(wp);
50a14534
EB
2849 return FALSE;
2850}
2851
165a4fa9
HR
2852static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2853{
2854 VikCoord one, two;
2855 struct LatLon one_ll, two_ll;
2856 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2857
2858 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2859 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2860 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2861 VikViewport *vvp = vik_window_viewport(vw);
2862 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2863 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2864 vik_coord_to_latlon(&one, &one_ll);
2865 vik_coord_to_latlon(&two, &two_ll);
2866 if (one_ll.lat > two_ll.lat) {
2867 maxmin[0].lat = one_ll.lat;
2868 maxmin[1].lat = two_ll.lat;
2869 }
2870 else {
2871 maxmin[0].lat = two_ll.lat;
2872 maxmin[1].lat = one_ll.lat;
2873 }
2874 if (one_ll.lon > two_ll.lon) {
2875 maxmin[0].lon = one_ll.lon;
2876 maxmin[1].lon = two_ll.lon;
2877 }
2878 else {
2879 maxmin[0].lon = two_ll.lon;
2880 maxmin[1].lon = one_ll.lon;
2881 }
120ab662
RN
2882 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2883 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
2884}
2885
2886static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2887{
2888 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2889 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2890 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2891
2892 trw_layer_find_maxmin (vtl, maxmin);
120ab662
RN
2893 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2894 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
2895}
2896
b3eb3b98
RN
2897#ifdef VIK_CONFIG_GEOTAG
2898static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2899{
2900 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2901 if ( wp )
2902 // Update directly - not changing the mtime
2903 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2904}
2905
2906static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2907{
2908 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2909 if ( wp )
2910 // Update directly
2911 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2912}
2913
2914/*
2915 * Use code in separate file for this feature as reasonably complex
2916 */
2917static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2918{
2919 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2920 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2921 // Unset so can be reverified later if necessary
2922 vtl->has_verified_thumbnails = FALSE;
2923
2924 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2925 vtl,
2926 track,
f182700c 2927 track->name );
b3eb3b98
RN
2928}
2929
2930static void trw_layer_geotagging ( gpointer lav[2] )
2931{
2932 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2933 // Unset so can be reverified later if necessary
2934 vtl->has_verified_thumbnails = FALSE;
2935
2936 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2937 vtl,
2938 NULL,
2939 NULL);
2940}
2941#endif
2942
16fc32f6
RN
2943// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2944
2945/*
2946 * Acquire into this TRW Layer straight from GPS Device
2947 */
2948static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2949{
2950 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2951 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2952 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2953 VikViewport *vvp = vik_window_viewport(vw);
2954
2955 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2956 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2957}
2958
55340efa 2959#ifdef VIK_CONFIG_GOOGLE
16fc32f6
RN
2960/*
2961 * Acquire into this TRW Layer from Google Directions
2962 */
2963static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2964{
2965 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2966 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2967 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2968 VikViewport *vvp = vik_window_viewport(vw);
2969
2970 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2971}
29332250 2972#endif
16fc32f6 2973
40f5740b
RN
2974#ifdef VIK_CONFIG_OPENSTREETMAP
2975/*
2976 * Acquire into this TRW Layer from OSM
2977 */
2978static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2979{
2980 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2981 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2982 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2983 VikViewport *vvp = vik_window_viewport(vw);
2984
2985 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2986}
2987#endif
2988
16fc32f6
RN
2989#ifdef VIK_CONFIG_GEOCACHES
2990/*
2991 * Acquire into this TRW Layer from Geocaching.com
2992 */
2993static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2994{
2995 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2996 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2997 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2998 VikViewport *vvp = vik_window_viewport(vw);
2999
3000 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
3001}
3002#endif
3003
68bab1bd
RN
3004#ifdef VIK_CONFIG_GEOTAG
3005/*
3006 * Acquire into this TRW Layer from images
3007 */
3008static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3009{
3010 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3011 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3012 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3013 VikViewport *vvp = vik_window_viewport(vw);
3014
3015 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3016 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3017
3018 // Reverify thumbnails as they may have changed
3019 vtl->has_verified_thumbnails = FALSE;
3020 trw_layer_verify_thumbnails ( vtl, NULL );
3021}
3022#endif
3023
e50758c7
RN
3024static void trw_layer_gps_upload ( gpointer lav[2] )
3025{
3026 gpointer pass_along[6];
3027 pass_along[0] = lav[0];
3028 pass_along[1] = lav[1];
3029 pass_along[2] = NULL; // No track - operate on the layer
3030 pass_along[3] = NULL;
3031 pass_along[4] = NULL;
3032 pass_along[5] = NULL;
3033
3034 trw_layer_gps_upload_any ( pass_along );
3035}
3036
3037/**
3038 * If pass_along[3] is defined that this will upload just that track
3039 */
3040static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3041{
3042 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3043 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3044
0d2b891f
RN
3045 // May not actually get a track here as pass_along[2&3] can be null
3046 VikTrack *track = NULL;
3047 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3048 gboolean xfer_all = FALSE;
e50758c7 3049
0d2b891f
RN
3050 if ( pass_along[2] ) {
3051 xfer_all = FALSE;
3052 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3053 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3054 xfer_type = RTE;
3055 }
3056 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3057 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3058 xfer_type = TRK;
3059 }
3060 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3061 xfer_type = WPT;
3062 }
3063 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3064 xfer_type = RTE;
3065 }
3066 }
3067 else if ( !pass_along[4] )
3068 xfer_all = TRUE; // i.e. whole layer
e50758c7 3069
0d2b891f 3070 if (track && !track->visible) {
e50758c7
RN
3071 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3072 return;
3073 }
3074
3075 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3076 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3077 GTK_DIALOG_DESTROY_WITH_PARENT,
3078 GTK_STOCK_OK,
3079 GTK_RESPONSE_ACCEPT,
3080 GTK_STOCK_CANCEL,
3081 GTK_RESPONSE_REJECT,
3082 NULL );
3083
3084 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3085 GtkWidget *response_w = NULL;
3086#if GTK_CHECK_VERSION (2, 20, 0)
3087 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3088#endif
3089
3090 if ( response_w )
3091 gtk_widget_grab_focus ( response_w );
3092
0d2b891f 3093 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
e50758c7
RN
3094
3095 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3096 datasource_gps_clean_up ( dgs );
3097 gtk_widget_destroy ( dialog );
3098 return;
3099 }
3100
3101 // Get info from reused datasource dialog widgets
3102 gchar* protocol = datasource_gps_get_protocol ( dgs );
3103 gchar* port = datasource_gps_get_descriptor ( dgs );
3104 // NB don't free the above strings as they're references to values held elsewhere
3105 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
0d2b891f 3106 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
e50758c7
RN
3107 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3108 gboolean turn_off = datasource_gps_get_off ( dgs );
3109
3110 gtk_widget_destroy ( dialog );
3111
3112 // When called from the viewport - work the corresponding layerspanel:
3113 if ( !vlp ) {
3114 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3115 }
3116
3117 // Apply settings to transfer to the GPS device
3118 vik_gps_comm ( vtl,
3119 track,
3120 GPS_UP,
3121 protocol,
3122 port,
3123 FALSE,
3124 vik_layers_panel_get_viewport (vlp),
3125 vlp,
3126 do_tracks,
0d2b891f 3127 do_routes,
e50758c7
RN
3128 do_waypoints,
3129 turn_off );
3130}
3131
d7ac7564
RN
3132/*
3133 * Acquire into this TRW Layer from any GPS Babel supported file
3134 */
3135static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3136{
3137 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3138 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3139 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3140 VikViewport *vvp = vik_window_viewport(vw);
3141
3142 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3143}
3144
50a14534
EB
3145static void trw_layer_new_wp ( gpointer lav[2] )
3146{
3147 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3148 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3149 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3150 instead return true if you want to update. */
3151 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 )
3152 vik_layers_panel_emit_update ( vlp );
3153}
3154
37615c52
RN
3155static void trw_layer_new_track ( gpointer lav[2] )
3156{
3157 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3158
3159 if ( ! vtl->current_track ) {
3160 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3161 vtl->current_track = vik_track_new();
3162 vtl->current_track->visible = TRUE;
3163 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3164
3165 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3166 }
3167}
3168
b1453c16
RN
3169static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3170{
3171 vtl->current_track = vik_track_new();
3172 vtl->current_track->visible = TRUE;
3173 vtl->current_track->is_route = TRUE;
3174 // By default make all routes red
3175 vtl->current_track->has_color = TRUE;
3176 gdk_color_parse ( "red", &vtl->current_track->color );
3177 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3178}
3179
0d2b891f
RN
3180static void trw_layer_new_route ( gpointer lav[2] )
3181{
3182 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3183
3184 if ( ! vtl->current_track ) {
3185 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
b1453c16 3186 new_route_create_common ( vtl, name );
e37b2a6d 3187 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
0d2b891f
RN
3188 }
3189}
3190
3191static void trw_layer_auto_routes_view ( gpointer lav[2] )
3192{
3193 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3194 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3195
3196 if ( g_hash_table_size (vtl->routes) > 0 ) {
3197 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3198 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3199 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3200 vik_layers_panel_emit_update ( vlp );
3201 }
3202}
3203
3204
37615c52
RN
3205static void trw_layer_finish_track ( gpointer lav[2] )
3206{
3207 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3208 vtl->current_track = NULL;
3209 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3210}
3211
535ed1ae
RN
3212static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3213{
3214 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3215 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3216
3217 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3218 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3219 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3220 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3221 vik_layers_panel_emit_update ( vlp );
3222 }
3223}
3224
fc59e8c7
RN
3225static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
3226{
3227 /* NB do not care if wp is visible or not */
3228 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3229}
3230
3231static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3232{
3233 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3234 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3235
3236 /* Only 1 waypoint - jump straight to it */
3237 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3238 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3239 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3240 }
3241 /* If at least 2 waypoints - find center and then zoom to fit */
3242 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3243 {
3244 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3245 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3246 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3247 }
3248
3249 vik_layers_panel_emit_update ( vlp );
3250}
3251
a7cd93ac 3252static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
50a14534
EB
3253{
3254 static gpointer pass_along[2];
3255 GtkWidget *item;
98fcbbdb 3256 GtkWidget *export_submenu;
50a14534
EB
3257 pass_along[0] = vtl;
3258 pass_along[1] = vlp;
3259
3260 item = gtk_menu_item_new();
3261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3262 gtk_widget_show ( item );
3263
37615c52 3264 if ( vtl->current_track ) {
0d2b891f
RN
3265 if ( vtl->current_track->is_route )
3266 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3267 else
3268 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
37615c52
RN
3269 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3270 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3271 gtk_widget_show ( item );
3272
3273 // Add separator
3274 item = gtk_menu_item_new ();
3275 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3276 gtk_widget_show ( item );
3277 }
3278
d6de71f9
RN
3279 /* Now with icons */
3280 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3281 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5a10c240
RN
3282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3283 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3284 gtk_widget_show ( item );
3285
48d28f21
RN
3286 GtkWidget *view_submenu = gtk_menu_new();
3287 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3288 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
535ed1ae
RN
3289 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3290 gtk_widget_show ( item );
48d28f21 3291 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
535ed1ae 3292
48d28f21
RN
3293 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3294 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3295 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3296 gtk_widget_show ( item );
3297
0d2b891f
RN
3298 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3300 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3301 gtk_widget_show ( item );
3302
48d28f21 3303 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
fc59e8c7 3304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
48d28f21 3305 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
fc59e8c7
RN
3306 gtk_widget_show ( item );
3307
d6de71f9
RN
3308 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3309 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
3310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3311 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3312 gtk_widget_show ( item );
3313
7306a492 3314 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
50a14534
EB
3315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3316 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3317 gtk_widget_show ( item );
3318
1bd88e66 3319 export_submenu = gtk_menu_new ();
d6de71f9
RN
3320 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3321 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
50a14534
EB
3322 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3323 gtk_widget_show ( item );
98fcbbdb 3324 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 3325
7306a492 3326 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
1bd88e66
GB
3327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3328 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3329 gtk_widget_show ( item );
50a14534 3330
7306a492 3331 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
50a14534 3332 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 3333 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
3334 gtk_widget_show ( item );
3335
7306a492 3336 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
561e6ad0 3337 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 3338 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
3339 gtk_widget_show ( item );
3340
ba9d0a00
RN
3341 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3343 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3344 gtk_widget_show ( item );
3345
ccccf356
RN
3346 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3347 item = gtk_menu_item_new_with_mnemonic ( external1 );
3348 g_free ( external1 );
3349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3350 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3351 gtk_widget_show ( item );
3352
ff02058b
RN
3353 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3354 item = gtk_menu_item_new_with_mnemonic ( external2 );
3355 g_free ( external2 );
3356 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3357 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3358 gtk_widget_show ( item );
3359
e7a8a2f4
RN
3360 GtkWidget *new_submenu = gtk_menu_new();
3361 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3362 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3363 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3364 gtk_widget_show(item);
3365 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3366
3367 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
d6de71f9 3368 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
50a14534 3369 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
e7a8a2f4 3370 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
50a14534 3371 gtk_widget_show ( item );
3e7553ae 3372
37615c52
RN
3373 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3374 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3376 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3377 gtk_widget_show ( item );
3378 // Make it available only when a new track *not* already in progress
3379 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3380
0d2b891f
RN
3381 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3382 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3384 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3385 gtk_widget_show ( item );
3386 // Make it available only when a new track *not* already in progress
3387 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3388
da03b29d 3389#ifdef VIK_CONFIG_GEONAMES
ba164b00 3390 GtkWidget *wikipedia_submenu = gtk_menu_new();
d6de71f9
RN
3391 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3392 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
165a4fa9
HR
3393 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3394 gtk_widget_show(item);
3395 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3396
d6de71f9
RN
3397 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3398 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
165a4fa9
HR
3399 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3400 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3401 gtk_widget_show ( item );
3402
d6de71f9
RN
3403 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3404 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
165a4fa9
HR
3405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3406 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3407 gtk_widget_show ( item );
da03b29d 3408#endif
165a4fa9 3409
b3eb3b98
RN
3410#ifdef VIK_CONFIG_GEOTAG
3411 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3412 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3413 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3414 gtk_widget_show ( item );
3415#endif
3416
16fc32f6 3417 GtkWidget *acquire_submenu = gtk_menu_new ();
d6de71f9
RN
3418 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3419 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
16fc32f6
RN
3420 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3421 gtk_widget_show ( item );
3422 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3423
3424 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3426 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3427 gtk_widget_show ( item );
3428
55340efa 3429#ifdef VIK_CONFIG_GOOGLE
55737d5b 3430 item = gtk_menu_item_new_with_mnemonic ( _("From Google _Directions...") );
16fc32f6
RN
3431 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3432 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3433 gtk_widget_show ( item );
29332250 3434#endif
16fc32f6 3435
40f5740b
RN
3436#ifdef VIK_CONFIG_OPENSTREETMAP
3437 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3438 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3439 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3440 gtk_widget_show ( item );
3441#endif
3442
16fc32f6
RN
3443#ifdef VIK_CONFIG_GEOCACHES
3444 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3446 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3447 gtk_widget_show ( item );
3448#endif
3449
68bab1bd
RN
3450#ifdef VIK_CONFIG_GEOTAG
3451 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3453 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3454 gtk_widget_show ( item );
3455#endif
3456
d7ac7564
RN
3457 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3458 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3459 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3460 gtk_widget_show ( item );
3461
e50758c7
RN
3462 GtkWidget *upload_submenu = gtk_menu_new ();
3463 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3464 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3465 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3466 gtk_widget_show ( item );
3467 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3468
0d2b891f
RN
3469 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3470 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3471 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3472 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3473 gtk_widget_show ( item );
3474
3e7553ae 3475#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9
RN
3476 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3477 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3e7553ae 3478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
e50758c7 3479 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3e7553ae
GB
3480 gtk_widget_show ( item );
3481#endif
28c82d8b 3482
c9a5cbf9 3483 GtkWidget *delete_submenu = gtk_menu_new ();
d6de71f9
RN
3484 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3485 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
3486 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3487 gtk_widget_show ( item );
3488 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3489
d6de71f9
RN
3490 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3491 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
3492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3493 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3494 gtk_widget_show ( item );
3495
d6de71f9
RN
3496 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
3498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3499 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3500 gtk_widget_show ( item );
3501
d6de71f9
RN
3502 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
3504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3505 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3506 gtk_widget_show ( item );
3507
d6de71f9
RN
3508 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3509 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
3510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3511 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3512 gtk_widget_show ( item );
3513
28c82d8b 3514 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 3515 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
3516 if ( item ) {
3517 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3518 gtk_widget_show ( item );
3519 }
3520
3521 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 3522 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
3523 if ( item ) {
3524 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3525 gtk_widget_show ( item );
3526 }
50a14534
EB
3527}
3528
c9570f86
RN
3529// Fake Waypoint UUIDs vi simple increasing integer
3530static guint wp_uuid = 0;
3531
50a14534
EB
3532void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3533{
c9570f86
RN
3534 wp_uuid++;
3535
3536 vik_waypoint_set_name (wp, name);
3537
50a14534
EB
3538 if ( VIK_LAYER(vtl)->realized )
3539 {
cd3f311e
RN
3540 // Do we need to create the sublayer:
3541 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3542 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3543 }
3544
c9570f86
RN
3545 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3546
3547 // Visibility column always needed for waypoints
50a14534 3548#ifdef VIK_CONFIG_ALPHABETIZED_TRW
70cefedc 3549 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, get_wp_sym_small (wp->symbol), TRUE, TRUE );
50a14534 3550#else
70cefedc 3551 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
50a14534 3552#endif
c9570f86
RN
3553 // Actual setting of visibility dependent on the waypoint
3554 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3555
3556 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
50a14534 3557 }
50a14534 3558
a8fe53f8 3559 highest_wp_number_add_wp(vtl, name);
c9570f86 3560 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
50a14534
EB
3561
3562}
3563
ce4bd1cf
RN
3564// Fake Track UUIDs vi simple increasing integer
3565static guint tr_uuid = 0;
3566
50a14534
EB
3567void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3568{
ce4bd1cf
RN
3569 tr_uuid++;
3570
3571 vik_track_set_name (t, name);
3572
50a14534
EB
3573 if ( VIK_LAYER(vtl)->realized )
3574 {
cd3f311e
RN
3575 // Do we need to create the sublayer:
3576 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3577 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3578 }
3579
ce4bd1cf
RN
3580 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3581 // Visibility column always needed for tracks
50a14534 3582#ifdef VIK_CONFIG_ALPHABETIZED_TRW
ce4bd1cf 3583 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 3584#else
ce4bd1cf 3585 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 3586#endif
ce4bd1cf
RN
3587 // Actual setting of visibility dependent on the track
3588 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3589
3590 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
50a14534 3591 }
50a14534 3592
ce4bd1cf 3593 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
b1453c16
RN
3594
3595 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
50a14534
EB
3596}
3597
0d2b891f
RN
3598// Fake Route UUIDs vi simple increasing integer
3599static guint rt_uuid = 0;
3600
3601void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3602{
3603 rt_uuid++;
3604
3605 vik_track_set_name (t, name);
3606
3607 if ( VIK_LAYER(vtl)->realized )
3608 {
3609 // Do we need to create the sublayer:
3610 if ( g_hash_table_size (vtl->routes) == 0 ) {
3611 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3612 }
3613
3614 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3615 // Visibility column always needed for tracks
3616#ifdef VIK_CONFIG_ALPHABETIZED_TRW
3617 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3618#else
3619 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3620#endif
3621 // Actual setting of visibility dependent on the track
3622 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3623
3624 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3625 }
3626
3627 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3628
b1453c16 3629 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
0d2b891f
RN
3630}
3631
50a14534 3632/* to be called whenever a track has been deleted or may have been changed. */
ce4bd1cf 3633void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 3634{
ce4bd1cf 3635 if (vtl->current_tp_track == trk )
50a14534 3636 trw_layer_cancel_current_tp ( vtl, FALSE );
50a14534 3637}
0d2b891f 3638
9748531a 3639gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
e890a6e6 3640{
0d2b891f
RN
3641 gint i = 2;
3642 gchar *newname = g_strdup(name);
3643
3644 gpointer id = NULL;
3645 do {
3646 id = NULL;
3647 switch ( sublayer_type ) {
3648 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3649 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3650 break;
3651 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3652 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3653 break;
3654 default:
3655 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3656 break;
3657 }
3658 // If found a name already in use try adding 1 to it and we try again
3659 if ( id ) {
3660 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3661 g_free(newname);
3662 newname = new_newname;
3663 i++;
3664 }
3665 } while ( id != NULL);
3666
e890a6e6
EB
3667 return newname;
3668}
50a14534 3669
805d282e
EB
3670void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3671{
c9570f86
RN
3672 // No more uniqueness of name forced when loading from a file
3673 // This now makes this function a little redunant as we just flow the parameters through
3674 vik_trw_layer_add_waypoint ( vtl, name, wp );
805d282e 3675}
c9570f86 3676
805d282e
EB
3677void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3678{
7ff7d728 3679 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
c3deba01 3680 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
7ff7d728 3681 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
bddd2056 3682 vik_track_free ( tr );
7ff7d728 3683 vtl->route_finder_append = FALSE; /* this means we have added it */
bddd2056 3684 } else {
ce4bd1cf
RN
3685
3686 // No more uniqueness of name forced when loading from a file
0d2b891f
RN
3687 if ( tr->is_route )
3688 vik_trw_layer_add_route ( vtl, name, tr );
3689 else
3690 vik_trw_layer_add_track ( vtl, name, tr );
bddd2056 3691
7ff7d728 3692 if ( vtl->route_finder_check_added_track ) {
c3deba01 3693 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ce4bd1cf 3694 vtl->route_finder_added_track = tr;
bddd2056
EB
3695 }
3696 }
805d282e
EB
3697}
3698
ce4bd1cf 3699static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
c48517ad 3700{
ce4bd1cf 3701 *l = g_list_append(*l, id);
c48517ad
AF
3702}
3703
ce4bd1cf
RN
3704/*
3705 * Move an item from one TRW layer to another TRW layer
3706 */
3707static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
c48517ad 3708{
c48517ad 3709 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
ce4bd1cf
RN
3710 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3711
9748531a 3712 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
ce4bd1cf 3713
03817fbf 3714 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
ce4bd1cf
RN
3715 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3716 vik_trw_layer_delete_track ( vtl_src, trk );
c48517ad 3717 }
c9570f86 3718
0d2b891f
RN
3719 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3720 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3721
3722 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3723
03817fbf 3724 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
0d2b891f
RN
3725 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3726 vik_trw_layer_delete_route ( vtl_src, trk );
3727 }
3728
ce4bd1cf
RN
3729 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3730 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
c9570f86 3731
9748531a 3732 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
c9570f86 3733
ce4bd1cf
RN
3734 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3735 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
c9570f86 3736 trw_layer_delete_waypoint ( vtl_src, wp );
c48517ad
AF
3737 }
3738}
3739
70a23263 3740static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
3741{
3742 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
3743 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3744
e4afc73a 3745 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
3746 GList *items = NULL;
3747 GList *iter;
e4afc73a 3748
c48517ad
AF
3749 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3750 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3751 }
3752 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3753 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3754 }
0d2b891f
RN
3755 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3756 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3757 }
3758
c48517ad
AF
3759 iter = items;
3760 while (iter) {
3761 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
0d2b891f
RN
3762 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3763 }
3764 else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3765 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
c48517ad 3766 } else {
0d2b891f 3767 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
c48517ad
AF
3768 }
3769 iter = iter->next;
e4afc73a 3770 }
c48517ad 3771 if (items)
b637058e 3772 g_list_free(items);
c48517ad
AF
3773 } else {
3774 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3775 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
3776 }
3777}
3778
ce4bd1cf
RN
3779typedef struct {
3780 VikTrack *trk; // input
3781 gpointer uuid; // output
3782} trku_udata;
3783
3784static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3785{
3786 trku_udata *user_data = udata;
3787 if ( trk == user_data->trk ) {
3788 user_data->uuid = id;
3789 return TRUE;
3790 }
3791 return FALSE;
3792}
3793
3794gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 3795{
50a14534 3796 gboolean was_visible = FALSE;
ce4bd1cf
RN
3797
3798 if ( trk && trk->name ) {
3799
3800 if ( trk == vtl->current_track ) {
50a14534 3801 vtl->current_track = NULL;
ce4bd1cf
RN
3802 vtl->current_tp_track = NULL;
3803 vtl->current_tp_id = NULL;
3804 vtl->moving_tp = FALSE;
77ad64fa 3805 }
ce4bd1cf
RN
3806
3807 was_visible = trk->visible;
3808
3809 if ( trk == vtl->route_finder_current_track )
7ff7d728
RN
3810 vtl->route_finder_current_track = NULL;
3811
ce4bd1cf
RN
3812 if ( trk == vtl->route_finder_added_track )
3813 vtl->route_finder_added_track = NULL;
3814
3815 trku_udata udata;
3816 udata.trk = trk;
3817 udata.uuid = NULL;
50a14534 3818
ce4bd1cf
RN
3819 // Hmmm, want key of it
3820 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
50a14534 3821
ce4bd1cf
RN
3822 if ( trkf && udata.uuid ) {
3823 /* could be current_tp, so we have to check */
3824 trw_layer_cancel_tps_of_track ( vtl, trk );
3825
3826 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3827
3828 if ( it ) {
3829 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3830 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3831 g_hash_table_remove ( vtl->tracks, udata.uuid );
cd3f311e
RN
3832
3833 // If last sublayer, then remove sublayer container
3834 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3835 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3836 }
ce4bd1cf
RN
3837 }
3838 }
50a14534
EB
3839 }
3840 return was_visible;
3841}
3842
0d2b891f
RN
3843gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3844{
3845 gboolean was_visible = FALSE;
3846
3847 if ( trk && trk->name ) {
3848
3849 if ( trk == vtl->current_track ) {
3850 vtl->current_track = NULL;
3851 vtl->current_tp_track = NULL;
3852 vtl->current_tp_id = NULL;
3853 vtl->moving_tp = FALSE;
3854 }
3855
3856 was_visible = trk->visible;
3857
3858 if ( trk == vtl->route_finder_current_track )
3859 vtl->route_finder_current_track = NULL;
3860
3861 if ( trk == vtl->route_finder_added_track )
3862 vtl->route_finder_added_track = NULL;
3863
3864 trku_udata udata;
3865 udata.trk = trk;
3866 udata.uuid = NULL;
3867
3868 // Hmmm, want key of it
3869 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3870
3871 if ( trkf && udata.uuid ) {
3872 /* could be current_tp, so we have to check */
3873 trw_layer_cancel_tps_of_track ( vtl, trk );
3874
3875 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3876
3877 if ( it ) {
3878 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3879 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3880 g_hash_table_remove ( vtl->routes, udata.uuid );
3881
3882 // If last sublayer, then remove sublayer container
3883 if ( g_hash_table_size (vtl->routes) == 0 ) {
3884 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3885 }
3886 }
3887 }
3888 }
3889 return was_visible;
3890}
3891
c9570f86 3892static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
e4afc73a
EB
3893{
3894 gboolean was_visible = FALSE;
e4afc73a 3895
c9570f86 3896 if ( wp && wp->name ) {
e4afc73a
EB
3897
3898 if ( wp == vtl->current_wp ) {
3899 vtl->current_wp = NULL;
c9570f86 3900 vtl->current_wp_id = NULL;
e4afc73a
EB
3901 vtl->moving_wp = FALSE;
3902 }
3903
3904 was_visible = wp->visible;
c9570f86
RN
3905
3906 wpu_udata udata;
3907 udata.wp = wp;
3908 udata.uuid = NULL;
3909
3910 // Hmmm, want key of it
3911 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3912
3913 if ( wpf && udata.uuid ) {
3914 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3915
3916 if ( it ) {
3917 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3918 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3919
3920 highest_wp_number_remove_wp(vtl, wp->name);
3921 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
cd3f311e
RN
3922
3923 // If last sublayer, then remove sublayer container
3924 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3925 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3926 }
c9570f86
RN
3927 }
3928 }
a8fe53f8 3929
e4afc73a
EB
3930 }
3931
3932 return was_visible;
3933}
3934
c9570f86
RN
3935// Only for temporary use by trw_layer_delete_waypoint_by_name
3936static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3937{
3938 wpu_udata *user_data = udata;
3939 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3940 user_data->uuid = id;
3941 return TRUE;
3942 }
3943 return FALSE;
3944}
3945
3946/*
3947 * Delete a waypoint by the given name
3948 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3949 * as there be multiple waypoints with the same name
3950 */
3951static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3952{
3953 wpu_udata udata;
3954 // Fake a waypoint with the given name
3955 udata.wp = vik_waypoint_new ();
3956 vik_waypoint_set_name (udata.wp, name);
3957 // Currently only the name is used in this waypoint find function
3958 udata.uuid = NULL;
3959
3960 // Hmmm, want key of it
3961 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3962
3963 vik_waypoint_free (udata.wp);
3964
3965 if ( wpf && udata.uuid )
3966 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3967 else
3968 return FALSE;
3969}
3970
ce4bd1cf
RN
3971typedef struct {
3972 VikTrack *trk; // input
3973 gpointer uuid; // output
3974} tpu_udata;
3975
3976// Only for temporary use by trw_layer_delete_track_by_name
3977static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3978{
3979 tpu_udata *user_data = udata;
3980 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3981 user_data->uuid = id;
3982 return TRUE;
3983 }
3984 return FALSE;
3985}
3986
3987/*
3988 * Delete a track by the given name
3989 * NOTE: ATM this will delete the first encountered Track with the specified name
0d2b891f 3990 * as there may be multiple tracks with the same name within the specified hash table
ce4bd1cf 3991 */
0d2b891f 3992static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
ce4bd1cf
RN
3993{
3994 tpu_udata udata;
3995 // Fake a track with the given name
3996 udata.trk = vik_track_new ();
3997 vik_track_set_name (udata.trk, name);
3998 // Currently only the name is used in this waypoint find function
3999 udata.uuid = NULL;
4000
4001 // Hmmm, want key of it
0d2b891f 4002 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
ce4bd1cf
RN
4003
4004 vik_track_free (udata.trk);
4005
0d2b891f
RN
4006 if ( trkf && udata.uuid ) {
4007 // This could be a little better written...
4008 if ( vtl->tracks == ht_tracks )
4009 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4010 if ( vtl->routes == ht_tracks )
4011 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4012 return FALSE;
4013 }
ce4bd1cf
RN
4014 else
4015 return FALSE;
4016}
4017
700b0908
QT
4018static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
4019{
4020 vik_treeview_item_delete (vt, it );
4021}
4022
0d2b891f
RN
4023void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4024{
4025
4026 vtl->current_track = NULL;
4027 vtl->route_finder_current_track = NULL;
4028 vtl->route_finder_added_track = NULL;
4029 if (vtl->current_tp_track)
4030 trw_layer_cancel_current_tp(vtl, FALSE);
4031
4032 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4033 g_hash_table_remove_all(vtl->routes_iters);
4034 g_hash_table_remove_all(vtl->routes);
4035
4036 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4037
4038 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4039}
4040
700b0908
QT
4041void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4042{
4043
4044 vtl->current_track = NULL;
7ff7d728 4045 vtl->route_finder_current_track = NULL;
ce4bd1cf
RN
4046 vtl->route_finder_added_track = NULL;
4047 if (vtl->current_tp_track)
700b0908 4048 trw_layer_cancel_current_tp(vtl, FALSE);
700b0908
QT
4049
4050 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4051 g_hash_table_remove_all(vtl->tracks_iters);
4052 g_hash_table_remove_all(vtl->tracks);
4053
cd3f311e
RN
4054 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4055
f01eebd4 4056 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
700b0908
QT
4057}
4058
4059void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4060{
4061 vtl->current_wp = NULL;
c9570f86 4062 vtl->current_wp_id = NULL;
700b0908
QT
4063 vtl->moving_wp = FALSE;
4064
a8fe53f8
EB
4065 highest_wp_number_reset(vtl);
4066
700b0908
QT
4067 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4068 g_hash_table_remove_all(vtl->waypoints_iters);
4069 g_hash_table_remove_all(vtl->waypoints);
4070
cd3f311e
RN
4071 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4072
f01eebd4 4073 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
700b0908
QT
4074}
4075
c9a5cbf9
RN
4076static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4077{
4078 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4079 // Get confirmation from the user
4080 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4081 _("Are you sure you want to delete all tracks in %s?"),
4082 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4083 vik_trw_layer_delete_all_tracks (vtl);
4084}
4085
0d2b891f 4086static void trw_layer_delete_all_routes ( gpointer lav[2] )
c9a5cbf9
RN
4087{
4088 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4089 // Get confirmation from the user
4090 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4091 _("Are you sure you want to delete all routes in %s?"),
4092 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4093 vik_trw_layer_delete_all_routes (vtl);
c9a5cbf9
RN
4094}
4095
0d2b891f
RN
4096static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4097{
4098 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4099 // Get confirmation from the user
4100 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4101 _("Are you sure you want to delete all waypoints in %s?"),
4102 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4103 vik_trw_layer_delete_all_waypoints (vtl);
4104}
4105
4106static void trw_layer_delete_item ( gpointer pass_along[6] )
50a14534
EB
4107{
4108 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4109 gboolean was_visible = FALSE;
dc2c040e 4110 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4111 {
c9570f86
RN
4112 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4113 if ( wp && wp->name ) {
4114 if ( GPOINTER_TO_INT ( pass_along[4]) )
4115 // Get confirmation from the user
4116 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4117 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4118 _("Are you sure you want to delete the waypoint \"%s\""),
4119 wp->name ) )
4120 return;
4121 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4122 }
50a14534 4123 }
0d2b891f 4124 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 4125 {
ce4bd1cf
RN
4126 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4127 if ( trk && trk->name ) {
4128 if ( GPOINTER_TO_INT ( pass_along[4]) )
4129 // Get confirmation from the user
4130 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
169acf64 4131 _("Are you sure you want to delete the track \"%s\""),
ce4bd1cf
RN
4132 trk->name ) )
4133 return;
4134 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4135 }
50a14534 4136 }
0d2b891f
RN
4137 else
4138 {
4139 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4140 if ( trk && trk->name ) {
4141 if ( GPOINTER_TO_INT ( pass_along[4]) )
4142 // Get confirmation from the user
4143 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4144 _("Are you sure you want to delete the route \"%s\""),
4145 trk->name ) )
4146 return;
4147 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4148 }
4149 }
50a14534 4150 if ( was_visible )
f01eebd4 4151 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
4152}
4153
4154
c9570f86 4155static void trw_layer_properties_item ( gpointer pass_along[7] )
50a14534
EB
4156{
4157 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 4158 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4159 {
c9570f86
RN
4160 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4161
4162 if ( wp && wp->name )
50a14534 4163 {
ac1bde8b 4164 gboolean updated = FALSE;
c9570f86 4165 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
50a14534 4166
70cefedc
RN
4167 if ( updated && pass_along[6] )
4168 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4169
ac1bde8b 4170 if ( updated && VIK_LAYER(vtl)->visible )
f01eebd4 4171 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
4172 }
4173 }
4174 else
4175 {
0d2b891f
RN
4176 VikTrack *tr;
4177 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4178 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4179 else
4180 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4181
ce4bd1cf 4182 if ( tr && tr->name )
50a14534 4183 {
21700912 4184 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
b1453c16
RN
4185 vtl,
4186 tr,
6bb72350 4187 pass_along[1], /* vlp */
b1453c16
RN
4188 pass_along[5], /* vvp */
4189 pass_along[6]); /* iter */
50a14534
EB
4190 }
4191 }
4192}
4193
b1453c16
RN
4194/*
4195 * Update the treeview of the track id - primarily to update the icon
4196 */
4197void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4198{
4199 trku_udata udata;
4200 udata.trk = trk;
4201 udata.uuid = NULL;
4202
4203 gpointer *trkf = NULL;
4204 if ( trk->is_route )
4205 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4206 else
4207 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4208
4209 if ( trkf && udata.uuid ) {
4210
4211 GtkTreeIter *iter = NULL;
4212 if ( trk->is_route )
4213 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4214 else
4215 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4216
4217 if ( iter ) {
4218 // TODO: Make this a function
4219 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4220 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4221 ((trk->color.green & 0xff00) << 8) |
4222 (trk->color.blue & 0xff00);
4223 gdk_pixbuf_fill ( pixbuf, pixel );
4224 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4225 g_object_unref (pixbuf);
4226 }
4227
4228 }
4229}
4230
6bb72350
RN
4231/*
4232 Parameter 1 -> VikLayersPanel
4233 Parameter 2 -> VikLayer
4234 Parameter 3 -> VikViewport
4235*/
4236static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4237{
4238 if ( vlp ) {
4239 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4240 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4241 }
4242 else {
4243 /* since vlp not set, vl & vvp should be valid instead! */
4244 if ( vl && vvp ) {
4245 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
f01eebd4 4246 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
6bb72350
RN
4247 }
4248 }
50a14534
EB
4249}
4250
6bb72350 4251static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
50a14534 4252{
0d2b891f
RN
4253 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4254 VikTrack *track;
4255 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4256 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4257 else
4258 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4259
4260 if ( track && track->trackpoints )
4261 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
50a14534
EB
4262}
4263
6bb72350 4264static void trw_layer_goto_track_center ( gpointer pass_along[6] )
50a14534 4265{
0d2b891f
RN
4266 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4267 VikTrack *track;
4268 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4269 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4270 else
4271 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4272
867f4673 4273 if ( track && track->trackpoints )
50a14534
EB
4274 {
4275 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4276 VikCoord coord;
867f4673 4277 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
50a14534
EB
4278 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4279 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
0d2b891f 4280 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
6bb72350 4281 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
50a14534
EB
4282 }
4283}
4284
2f5d7ea1
RN
4285static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4286{
4287 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4288 VikTrack *trk;
4289 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4290 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4291 else
4292 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4293
4294 if ( !trk )
4295 return;
4296
4297 // Converting a track to a route can be a bit more complicated,
4298 // so give a chance to change our minds:
4299 if ( !trk->is_route &&
4300 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4301 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4302
4303 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4304 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4305 return;
4306}
4307
4308 // Copy it
03817fbf 4309 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
2f5d7ea1
RN
4310
4311 // Convert
4312 trk_copy->is_route = !trk_copy->is_route;
4313
4314 // ATM can't set name to self - so must create temporary copy
4315 gchar *name = g_strdup ( trk_copy->name );
4316
4317 // Delete old one and then add new one
4318 if ( trk->is_route ) {
4319 vik_trw_layer_delete_route ( vtl, trk );
4320 vik_trw_layer_add_track ( vtl, name, trk_copy );
4321 }
4322 else {
4323 // Extra route conversion bits...
4324 vik_track_merge_segments ( trk_copy );
4325 vik_track_to_routepoints ( trk_copy );
4326
4327 vik_trw_layer_delete_track ( vtl, trk );
4328 vik_trw_layer_add_route ( vtl, name, trk_copy );
4329 }
4330 g_free ( name );
4331
4332 // Update in case color of track / route changes when moving between sublayers
4333 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4334}
4335
4336
8fb71d6c
EB
4337static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4338{
4339 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
0d2b891f
RN
4340 VikTrack *track;
4341 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4342 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4343 else
4344 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4345
4346 if ( !track )
4347 return;
8fb71d6c
EB
4348
4349 vtl->current_track = track;
e37b2a6d 4350 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
8fb71d6c
EB
4351
4352 if ( track->trackpoints )
6bb72350 4353 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
8fb71d6c
EB
4354}
4355
55340efa 4356#ifdef VIK_CONFIG_GOOGLE
e3154bef 4357/**
7ff7d728 4358 * extend a track using route finder
e3154bef 4359 */
7ff7d728 4360static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
a7955c1d
AM
4361{
4362 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
008e972c
RN
4363 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4364 if ( !track )
4365 return;
a7955c1d
AM
4366 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4367
4368 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
7ff7d728
RN
4369 vtl->route_finder_coord = last_coord;
4370 vtl->route_finder_current_track = track;
4371 vtl->route_finder_started = TRUE;
a7955c1d
AM
4372
4373 if ( track->trackpoints )
6bb72350 4374 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
a7955c1d
AM
4375
4376}
ebf1bd39 4377#endif
a7955c1d 4378
ad0a8c2d
EB
4379static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4380{
4381 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4382 /* Also warn if overwrite old elevation data */
0d2b891f
RN
4383 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4384 VikTrack *track;
4385 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4386 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4387 else
4388 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
ad0a8c2d 4389
0d2b891f
RN
4390 if ( track )
4391 vik_track_apply_dem_data ( track );
ad0a8c2d
EB
4392}
4393
50a14534
EB
4394static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4395{
0d2b891f
RN
4396 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4397 VikTrack *track;
4398 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4399 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4400 else
4401 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4402
4403 if ( !track )
4404 return;
4405
4406 GList *trps = track->trackpoints;
50a14534
EB
4407 if ( !trps )
4408 return;
111fa174 4409 trps = g_list_last(trps);
6bb72350 4410 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
50a14534
EB
4411}
4412
6bb72350 4413static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
03e7da75 4414{
0d2b891f
RN
4415 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4416 VikTrack *track;
4417 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4418 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4419 else
4420 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4421
4422 if ( !track )
4423 return;
4424
4425 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
03e7da75
RN
4426 if ( !vtp )
4427 return;
6bb72350 4428 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
03e7da75
RN
4429}
4430
6bb72350 4431static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
c28faca8 4432{
0d2b891f
RN
4433 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4434 VikTrack *track;
4435 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4436 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4437 else
4438 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4439
4440 if ( !track )
4441 return;
4442
4443 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
c28faca8
RN
4444 if ( !vtp )
4445 return;
6bb72350 4446 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
c28faca8
RN
4447}
4448
6bb72350 4449static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
c28faca8 4450{
0d2b891f
RN
4451 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4452 VikTrack *track;
4453 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4454 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4455 else
4456 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4457
4458 if ( !track )
4459 return;
4460
4461 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
c28faca8
RN
4462 if ( !vtp )
4463 return;
6bb72350 4464 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
c28faca8 4465}
111fa174 4466
116ea336 4467/*
469113fb 4468 * Automatically change the viewport to center on the track and zoom to see the extent of the track
116ea336
RN
4469 */
4470static void trw_layer_auto_track_view ( gpointer pass_along[6] )
469113fb 4471{
0d2b891f
RN
4472 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4473 VikTrack *trk;
4474 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4475 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4476 else
4477 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4478
867f4673 4479 if ( trk && trk->trackpoints )
469113fb
RN
4480 {
4481 struct LatLon maxmin[2] = { {0,0}, {0,0} };
867f4673 4482 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
6bb72350
RN
4483 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4484 if ( pass_along[1] )
4485 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4486 else
f01eebd4 4487 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
469113fb
RN
4488 }
4489}
4490
c95d6b00
RN
4491static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4492{
4493 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4494 trw_layer_tpwin_init ( vtl );
4495}
4496
111fa174
AF
4497/*************************************
4498 * merge/split by time routines
4499 *************************************/
4500
15f45edc 4501/* called for each key in track hash table.
5780603d 4502 * If the current track has the same time stamp type, add it to the result,
15f45edc
QT
4503 * except the one pointed by "exclude".
4504 * set exclude to NULL if there is no exclude to check.
5780603d 4505 * Note that the result is in reverse (for performance reasons).
15f45edc
QT
4506 */
4507typedef struct {
4508 GList **result;
4509 GList *exclude;
5780603d 4510 gboolean with_timestamps;
15f45edc 4511} twt_udata;
5780603d 4512static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
15f45edc
QT
4513{
4514 twt_udata *user_data = udata;
4515 VikTrackpoint *p1, *p2;
4516
4517 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4518 return;
4519 }
4520
4521 if (VIK_TRACK(value)->trackpoints) {
4522 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4523 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4524
5780603d
RN
4525 if ( user_data->with_timestamps ) {
4526 if (!p1->has_timestamp || !p2->has_timestamp) {
4527 return;
4528 }
4529 }
4530 else {
4531 // Don't add tracks with timestamps when getting non timestamp tracks
4532 if (p1->has_timestamp || p2->has_timestamp) {
4533 return;
4534 }
15f45edc 4535 }
15f45edc
QT
4536 }
4537
4538 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4539}
4540
111fa174
AF
4541/* called for each key in track hash table. if original track user_data[1] is close enough
4542 * to the passed one, add it to list in user_data[0]
4543 */
fc92c977 4544static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
111fa174
AF
4545{
4546 time_t t1, t2;
4547 VikTrackpoint *p1, *p2;
fc92c977 4548 VikTrack *trk = VIK_TRACK(value);
111fa174
AF
4549
4550 GList **nearby_tracks = ((gpointer *)user_data)[0];
fc92c977 4551 GList *tpoints = ((gpointer *)user_data)[1];
111fa174 4552
a61b2619
AF
4553 /* outline:
4554 * detect reasons for not merging, and return
4555 * if no reason is found not to merge, then do it.
4556 */
111fa174 4557
fc92c977
RN
4558 // Exclude the original track from the compiled list
4559 if (trk->trackpoints == tpoints) {
111fa174
AF
4560 return;
4561 }
4562
fc92c977
RN
4563 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4564 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
111fa174 4565
fc92c977
RN
4566 if (trk->trackpoints) {
4567 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4568 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
a61b2619
AF
4569
4570 if (!p1->has_timestamp || !p2->has_timestamp) {
fc92c977 4571 //g_print("no timestamp\n");
a61b2619
AF
4572 return;
4573 }
111fa174 4574
fc92c977
RN
4575 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4576 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4577 if (! (abs(t1 - p2->timestamp) < threshold ||
a61b2619 4578 /* p1 p2 t1 t2 */
fc92c977 4579 abs(p1->timestamp - t2) < threshold)
a61b2619
AF
4580 /* t1 t2 p1 p2 */
4581 ) {
4582 return;
4583 }
111fa174 4584 }
a61b2619 4585
fc92c977 4586 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
111fa174
AF
4587}
4588
4589/* comparison function used to sort tracks; a and b are hash table keys */
02b5d347 4590/* Not actively used - can be restored if needed
111fa174
AF
4591static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4592{
4593 GHashTable *tracks = user_data;
4594 time_t t1, t2;
4595
4596 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4597 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4598
4599 if (t1 < t2) return -1;
4600 if (t1 > t2) return 1;
4601 return 0;
4602}
02b5d347 4603*/
111fa174
AF
4604
4605/* comparison function used to sort trackpoints */
4606static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4607{
4608 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4609
4610 if (t1 < t2) return -1;
4611 if (t1 > t2) return 1;
4612 return 0;
4613}
4614
fb2306f7
RN
4615/**
4616 * comparison function which can be used to sort tracks or waypoints by name
4617 */
4618static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4619{
4620 const gchar* namea = (const gchar*) a;
4621 const gchar* nameb = (const gchar*) b;
4622 if ( namea == NULL || nameb == NULL)
4623 return 0;
4624 else
4625 // Same sort method as used in the vik_treeview_*_alphabetize functions
4626 return strcmp ( namea, nameb );
4627}
fb2306f7 4628
5780603d
RN
4629/**
4630 * Attempt to merge selected track with other tracks specified by the user
4631 * Tracks to merge with must be of the same 'type' as the selected track -
4632 * either all with timestamps, or all without timestamps
4633 */
291edcab
HR
4634static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4635{
4636 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5780603d 4637 GList *other_tracks = NULL;
0d2b891f
RN
4638 GHashTable *ght_tracks;
4639 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4640 ght_tracks = vtl->routes;
4641 else
4642 ght_tracks = vtl->tracks;
4643
4644 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4645
4646 if ( !track )
4647 return;
15f45edc 4648
5780603d 4649 if ( !track->trackpoints )
15f45edc 4650 return;
15f45edc 4651
c1564279 4652 twt_udata udata;
5780603d 4653 udata.result = &other_tracks;
c1564279 4654 udata.exclude = track->trackpoints;
5780603d
RN
4655 // Allow merging with 'similar' time type time tracks
4656 // i.e. either those times, or those without
4657 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
291edcab 4658
0d2b891f 4659 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5780603d
RN
4660 other_tracks = g_list_reverse(other_tracks);
4661
4662 if ( !other_tracks ) {
4663 if ( udata.with_timestamps )
4664 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4665 else
4666 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
15f45edc
QT
4667 return;
4668 }
4669
8352326e 4670 // Sort alphabetically for user presentation
ce4bd1cf
RN
4671 // Convert into list of names for usage with dialog function
4672 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4673 GList *other_tracks_names = NULL;
4674 GList *iter = g_list_first ( other_tracks );
4675 while ( iter ) {
0d2b891f 4676 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
ce4bd1cf
RN
4677 iter = g_list_next ( iter );
4678 }
4679
4680 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
8352326e 4681
7767aa02 4682 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4683 other_tracks_names,
4684 TRUE,
4685 _("Merge with..."),
4686 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5780603d 4687 g_list_free(other_tracks);
ce4bd1cf 4688 g_list_free(other_tracks_names);
291edcab 4689
7767aa02 4690 if (merge_list)
291edcab 4691 {
7767aa02
QT
4692 GList *l;
4693 for (l = merge_list; l != NULL; l = g_list_next(l)) {
0d2b891f
RN
4694 VikTrack *merge_track;
4695 if ( track->is_route )
4696 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4697 else
4698 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4699
7767aa02
QT
4700 if (merge_track) {
4701 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4702 merge_track->trackpoints = NULL;
0d2b891f
RN
4703 if ( track->is_route )
4704 vik_trw_layer_delete_route (vtl, merge_track);
4705 else
4706 vik_trw_layer_delete_track (vtl, merge_track);
7767aa02
QT
4707 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4708 }
291edcab 4709 }
7767aa02
QT
4710 /* TODO: free data before free merge_list */
4711 for (l = merge_list; l != NULL; l = g_list_next(l))
4712 g_free(l->data);
4713 g_list_free(merge_list);
f01eebd4 4714 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
291edcab 4715 }
291edcab
HR
4716}
4717
9c34f614
RN
4718// c.f. trw_layer_sorted_track_id_by_name_list
4719// but don't add the specified track to the list (normally current track)
4720static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4721{
4722 twt_udata *user_data = udata;
4723
4724 // Skip self
4725 if (trk->trackpoints == user_data->exclude) {
4726 return;
4727 }
4728
4729 // Sort named list alphabetically
4730 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4731}
4732
4733/**
0d2b891f 4734 * Join - this allows combining 'tracks' and 'track routes'
9c34f614
RN
4735 * i.e. doesn't care about whether tracks have consistent timestamps
4736 * ATM can only append one track at a time to the currently selected track
4737 */
4738static void trw_layer_append_track ( gpointer pass_along[6] )
4739{
4740
4741 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
4742 VikTrack *trk;
4743 GHashTable *ght_tracks;
4744 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4745 ght_tracks = vtl->routes;
4746 else
4747 ght_tracks = vtl->tracks;
4748
4749 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4750
4751 if ( !trk )
4752 return;
9c34f614
RN
4753
4754 GList *other_tracks_names = NULL;
4755
4756 // Sort alphabetically for user presentation
4757 // Convert into list of names for usage with dialog function
4758 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4759 twt_udata udata;
4760 udata.result = &other_tracks_names;
4761 udata.exclude = trk->trackpoints;
4762
0d2b891f 4763 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
9c34f614
RN
4764
4765 // Note the limit to selecting one track only
4766 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4767 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4768 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4769 other_tracks_names,
4770 FALSE,
4771 trk->is_route ? _("Append Route"): _("Append Track"),
4772 trk->is_route ? _("Select the route to append after the current route") :
4773 _("Select the track to append after the current track") );
9c34f614
RN
4774
4775 g_list_free(other_tracks_names);
4776
4777 // It's a list, but shouldn't contain more than one other track!
4778 if ( append_list ) {
4779 GList *l;
4780 for (l = append_list; l != NULL; l = g_list_next(l)) {
4781 // TODO: at present this uses the first track found by name,
4782 // which with potential multiple same named tracks may not be the one selected...
0d2b891f
RN
4783 VikTrack *append_track;
4784 if ( trk->is_route )
4785 append_track = vik_trw_layer_get_route ( vtl, l->data );
4786 else
4787 append_track = vik_trw_layer_get_track ( vtl, l->data );
4788
9c34f614
RN
4789 if ( append_track ) {
4790 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4791 append_track->trackpoints = NULL;
0d2b891f
RN
4792 if ( trk->is_route )
4793 vik_trw_layer_delete_route (vtl, append_track);
4794 else
4795 vik_trw_layer_delete_track (vtl, append_track);
9c34f614
RN
4796 }
4797 }
4798 for (l = append_list; l != NULL; l = g_list_next(l))
4799 g_free(l->data);
4800 g_list_free(append_list);
4801 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4802 }
4803}
4804
6b5b6c47
RN
4805/**
4806 * Very similar to trw_layer_append_track for joining
4807 * but this allows selection from the 'other' list
4808 * If a track is selected, then is shows routes and joins the selected one
4809 * If a route is selected, then is shows tracks and joins the selected one
4810 */
4811static void trw_layer_append_other ( gpointer pass_along[6] )
4812{
4813
4814 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4815 VikTrack *trk;
4816 GHashTable *ght_mykind, *ght_others;
4817 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4818 ght_mykind = vtl->routes;
4819 ght_others = vtl->tracks;
4820 }
4821 else {
4822 ght_mykind = vtl->tracks;
4823 ght_others = vtl->routes;
4824 }
4825
4826 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4827
4828 if ( !trk )
4829 return;
4830
4831 GList *other_tracks_names = NULL;
4832
4833 // Sort alphabetically for user presentation
4834 // Convert into list of names for usage with dialog function
4835 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4836 twt_udata udata;
4837 udata.result = &other_tracks_names;
4838 udata.exclude = trk->trackpoints;
4839
4840 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4841
4842 // Note the limit to selecting one track only
4843 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4844 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4845 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4846 other_tracks_names,
4847 FALSE,
4848 trk->is_route ? _("Append Track"): _("Append Route"),
4849 trk->is_route ? _("Select the track to append after the current route") :
4850 _("Select the route to append after the current track") );
4851
4852 g_list_free(other_tracks_names);
4853
4854 // It's a list, but shouldn't contain more than one other track!
4855 if ( append_list ) {
4856 GList *l;
4857 for (l = append_list; l != NULL; l = g_list_next(l)) {
4858 // TODO: at present this uses the first track found by name,
4859 // which with potential multiple same named tracks may not be the one selected...
4860
4861 // Get FROM THE OTHER TYPE list
4862 VikTrack *append_track;
4863 if ( trk->is_route )
4864 append_track = vik_trw_layer_get_track ( vtl, l->data );
4865 else
4866 append_track = vik_trw_layer_get_route ( vtl, l->data );
4867
4868 if ( append_track ) {
4869
4870 if ( !append_track->is_route &&
4871 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4872 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4873
4874 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4875 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4876 vik_track_merge_segments ( append_track );
4877 vik_track_to_routepoints ( append_track );
4878 }
4879 else {
4880 break;
4881 }
4882 }
4883
4884 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4885 append_track->trackpoints = NULL;
4886
4887 // Delete copied which is FROM THE OTHER TYPE list
4888 if ( trk->is_route )
4889 vik_trw_layer_delete_track (vtl, append_track);
4890 else
4891 vik_trw_layer_delete_route (vtl, append_track);
4892 }
4893 }
4894 for (l = append_list; l != NULL; l = g_list_next(l))
4895 g_free(l->data);
4896 g_list_free(append_list);
4897 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4898 }
4899}
4900
24774c43
RN
4901/* merge by segments */
4902static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4903{
4904 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4905 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4906 guint segments = vik_track_merge_segments ( trk );
4907 // NB currently no need to redraw as segments not actually shown on the display
4908 // However inform the user of what happened:
4909 gchar str[64];
4910 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4911 g_snprintf(str, 64, tmp_str, segments);
4912 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4913}
4914
111fa174
AF
4915/* merge by time routine */
4916static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4917{
a61b2619 4918 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
a61b2619 4919
02b5d347 4920 //time_t t1, t2;
111fa174 4921
c1564279 4922 GList *tracks_with_timestamp = NULL;
ce4bd1cf
RN
4923 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4924 if (orig_trk->trackpoints &&
4925 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
c1564279 4926 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
c1564279
RN
4927 return;
4928 }
4929
4930 twt_udata udata;
4931 udata.result = &tracks_with_timestamp;
ce4bd1cf 4932 udata.exclude = orig_trk->trackpoints;
5780603d
RN
4933 udata.with_timestamps = TRUE;
4934 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
c1564279
RN
4935 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4936
4937 if (!tracks_with_timestamp) {
4938 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
c1564279
RN
4939 return;
4940 }
4941 g_list_free(tracks_with_timestamp);
4942
fc92c977
RN
4943 static guint threshold_in_minutes = 1;
4944 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4945 _("Merge Threshold..."),
4946 _("Merge when time between tracks less than:"),
4947 &threshold_in_minutes)) {
111fa174
AF
4948 return;
4949 }
4950
fc92c977
RN
4951 // keep attempting to merge all tracks until no merges within the time specified is possible
4952 gboolean attempt_merge = TRUE;
4953 GList *nearby_tracks = NULL;
4954 GList *trps;
4955 static gpointer params[3];
4956
4957 while ( attempt_merge ) {
4958
4959 // Don't try again unless tracks have changed
4960 attempt_merge = FALSE;
111fa174 4961
ce4bd1cf 4962 trps = orig_trk->trackpoints;
111fa174
AF
4963 if ( !trps )
4964 return;
4965
111fa174
AF
4966 if (nearby_tracks) {
4967 g_list_free(nearby_tracks);
4968 nearby_tracks = NULL;
4969 }
4970
02b5d347
RN
4971 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4972 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
111fa174 4973
70a23263 4974 /* g_print("Original track times: %d and %d\n", t1, t2); */
111fa174 4975 params[0] = &nearby_tracks;
fc92c977
RN
4976 params[1] = (gpointer)trps;
4977 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
111fa174
AF
4978
4979 /* get a list of adjacent-in-time tracks */
fc92c977 4980 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
111fa174 4981
111fa174 4982 /* merge them */
fc92c977
RN
4983 GList *l = nearby_tracks;
4984 while ( l ) {
4985 /*
4986#define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4987#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4988 time_t t1, t2;
4989 t1 = get_first_trackpoint(l)->timestamp;
4990 t2 = get_last_trackpoint(l)->timestamp;
ce4bd1cf
RN
4991#undef get_first_trackpoint
4992#undef get_last_trackpoint
fc92c977
RN
4993 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4994 */
111fa174 4995
fc92c977
RN
4996 /* remove trackpoints from merged track, delete track */
4997 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4998 VIK_TRACK(l->data)->trackpoints = NULL;
4999 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
111fa174 5000
fc92c977
RN
5001 // Tracks have changed, therefore retry again against all the remaining tracks
5002 attempt_merge = TRUE;
ce4bd1cf 5003
fc92c977 5004 l = g_list_next(l);
111fa174 5005 }
fc92c977
RN
5006
5007 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5008 }
5009
111fa174 5010 g_list_free(nearby_tracks);
f01eebd4 5011 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
111fa174
AF
5012}
5013
84d3d9f9 5014/**
9914238e 5015 * Split a track at the currently selected trackpoint
84d3d9f9 5016 */
0d2b891f 5017static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
84d3d9f9
RN
5018{
5019 if ( !vtl->current_tpl )
5020 return;
5021
5022 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
0d2b891f 5023 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
9914238e 5024 if ( name ) {
03817fbf 5025 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
84d3d9f9
RN
5026 GList *newglist = g_list_alloc ();
5027 newglist->prev = NULL;
5028 newglist->next = vtl->current_tpl->next;
5029 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5030 tr->trackpoints = newglist;
5031
5032 vtl->current_tpl->next->prev = newglist; /* end old track here */
5033 vtl->current_tpl->next = NULL;
5034
5035 vtl->current_tpl = newglist; /* change tp to first of new track. */
5036 vtl->current_tp_track = tr;
5037
0d2b891f
RN
5038 if ( tr->is_route )
5039 vik_trw_layer_add_route ( vtl, name, tr );
5040 else
5041 vik_trw_layer_add_track ( vtl, name, tr );
84d3d9f9
RN
5042
5043 trku_udata udata;
5044 udata.trk = tr;
5045 udata.uuid = NULL;
5046
5047 // Also need id of newly created track
0d2b891f
RN
5048 gpointer *trkf;
5049 if ( tr->is_route )
5050 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5051 else
5052 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5053
84d3d9f9
RN
5054 if ( trkf && udata.uuid )
5055 vtl->current_tp_id = udata.uuid;
5056 else
5057 vtl->current_tp_id = NULL;
5058
5059 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5060 }
5061 }
5062}
5063
111fa174
AF
5064/* split by time routine */
5065static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5066{
784b6042
RN
5067 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5068 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
111fa174
AF
5069 GList *trps = track->trackpoints;
5070 GList *iter;
5071 GList *newlists = NULL;
5072 GList *newtps = NULL;
111fa174
AF
5073 static guint thr = 1;
5074
5075 time_t ts, prev_ts;
5076
5077 if ( !trps )
5078 return;
5079
5080 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4c77d5e0
GB
5081 _("Split Threshold..."),
5082 _("Split when time between trackpoints exceeds:"),
111fa174
AF
5083 &thr)) {
5084 return;
5085 }
5086
5087 /* iterate through trackpoints, and copy them into new lists without touching original list */
5088 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5089 iter = trps;
5090
5091 while (iter) {
5092 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5093 if (ts < prev_ts) {
70a23263 5094 g_print("panic: ts < prev_ts: this should never happen!\n");
111fa174
AF
5095 return;
5096 }
5097 if (ts - prev_ts > thr*60) {
5098 /* flush accumulated trackpoints into new list */
aa9887a1 5099 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
5100 newtps = NULL;
5101 }
5102
5103 /* accumulate trackpoint copies in newtps, in reverse order */
5104 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5105 prev_ts = ts;
5106 iter = g_list_next(iter);
5107 }
5108 if (newtps) {
aa9887a1 5109 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
5110 }
5111
5112 /* put lists of trackpoints into tracks */
5113 iter = newlists;
c9d8f273
RN
5114 // Only bother updating if the split results in new tracks
5115 if (g_list_length (newlists) > 1) {
5116 while (iter) {
5117 gchar *new_tr_name;
5118 VikTrack *tr;
111fa174 5119
03817fbf 5120 tr = vik_track_copy ( track, FALSE );
c9d8f273 5121 tr->trackpoints = (GList *)(iter->data);
111fa174 5122
784b6042
RN
5123 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5124 vik_trw_layer_add_track(vtl, new_tr_name, tr);
c9d8f273 5125 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
111fa174
AF
5126 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5127
c9d8f273
RN
5128 iter = g_list_next(iter);
5129 }
ce4bd1cf 5130 // Remove original track and then update the display
784b6042 5131 vik_trw_layer_delete_track (vtl, track);
f01eebd4 5132 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
111fa174
AF
5133 }
5134 g_list_free(newlists);
111fa174
AF
5135}
5136
af2341f3
RN
5137/**
5138 * Split a track by the number of points as specified by the user
5139 */
5140static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5141{
5142 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
5143 VikTrack *track;
5144 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5145 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5146 else
5147 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5148
5149 if ( !track )
5150 return;
af2341f3
RN
5151
5152 // Check valid track
5153 GList *trps = track->trackpoints;
5154 if ( !trps )
5155 return;
5156
5157 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5158 _("Split Every Nth Point"),
5159 _("Split on every Nth point:"),
5160 250, // Default value as per typical limited track capacity of various GPS devices
5161 2, // Min
5162 65536, // Max
5163 5); // Step
5164 // Was a valid number returned?
5165 if (!points)
5166 return;
5167
5168 // Now split...
5169 GList *iter;
5170 GList *newlists = NULL;
5171 GList *newtps = NULL;
5172 gint count = 0;
5173 iter = trps;
5174
5175 while (iter) {
5176 /* accumulate trackpoint copies in newtps, in reverse order */
5177 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5178 count++;
5179 if (count >= points) {
5180 /* flush accumulated trackpoints into new list */
5181 newlists = g_list_append(newlists, g_list_reverse(newtps));
5182 newtps = NULL;
5183 count = 0;
5184 }
5185 iter = g_list_next(iter);
5186 }
5187
5188 // If there is a remaining chunk put that into the new split list
5189 // This may well be the whole track if no split points were encountered
5190 if (newtps) {
5191 newlists = g_list_append(newlists, g_list_reverse(newtps));
5192 }
5193
5194 /* put lists of trackpoints into tracks */
5195 iter = newlists;
af2341f3
RN
5196 // Only bother updating if the split results in new tracks
5197 if (g_list_length (newlists) > 1) {
5198 while (iter) {
5199 gchar *new_tr_name;
5200 VikTrack *tr;
5201
03817fbf 5202 tr = vik_track_copy ( track, FALSE );
af2341f3
RN
5203 tr->trackpoints = (GList *)(iter->data);
5204
0d2b891f
RN
5205 if ( track->is_route ) {
5206 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5207 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5208 }
5209 else {
5210 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5211 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5212 }
af2341f3
RN
5213 iter = g_list_next(iter);
5214 }
5215 // Remove original track and then update the display
0d2b891f
RN
5216 if ( track->is_route )
5217 vik_trw_layer_delete_route (vtl, track);
5218 else
5219 vik_trw_layer_delete_track (vtl, track);
f01eebd4 5220 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
af2341f3
RN
5221 }
5222 g_list_free(newlists);
5223}
5224
a4aefe69
RN
5225/**
5226 * Split a track at the currently selected trackpoint
5227 */
5228static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5229{
5230 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
5231 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5232 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
a4aefe69 5233}
98e87078
RN
5234
5235/**
5236 * Split a track by its segments
0d2b891f 5237 * Routes do not have segments so don't call this for routes
98e87078
RN
5238 */
5239static void trw_layer_split_segments ( gpointer pass_along[6] )
5240{
5241 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
5242 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5243
5244 if ( !trk )
5245 return;
5246
98e87078
RN
5247 guint ntracks;
5248
5249 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5250 gchar *new_tr_name;
5251 guint i;
5252 for ( i = 0; i < ntracks; i++ ) {
5253 if ( tracks[i] ) {
5254 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5255 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5256 }
5257 }
5258 if ( tracks ) {
5259 g_free ( tracks );
5260 // Remove original track
5261 vik_trw_layer_delete_track ( vtl, trk );
5262 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5263 }
5264 else {
5265 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5266 }
5267}
111fa174
AF
5268/* end of split/merge routines */
5269
b6eda120
RN
5270/**
5271 * Delete adjacent track points at the same position
5272 * AKA Delete Dulplicates on the Properties Window
5273 */
5274static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5275{
5276 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
5277 VikTrack *trk;
5278 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5279 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5280 else
5281 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5282
5283 if ( !trk )
5284 return;
b6eda120
RN
5285
5286 gulong removed = vik_track_remove_dup_points ( trk );
5287
5288 // Track has been updated so update tps:
5289 trw_layer_cancel_tps_of_track ( vtl, trk );
5290
5291 // Inform user how much was deleted as it's not obvious from the normal view
5292 gchar str[64];
5293 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5294 g_snprintf(str, 64, tmp_str, removed);
5295 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5296
5297 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5298}
5299
6579ca1f
RN
5300/**
5301 * Delete adjacent track points with the same timestamp
5302 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5303 */
5304static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5305{
5306 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
5307 VikTrack *trk;
5308 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5309 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5310 else
5311 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5312
5313 if ( !trk )
5314 return;
6579ca1f
RN
5315
5316 gulong removed = vik_track_remove_same_time_points ( trk );
5317
5318 // Track has been updated so update tps:
5319 trw_layer_cancel_tps_of_track ( vtl, trk );
5320
5321 // Inform user how much was deleted as it's not obvious from the normal view
5322 gchar str[64];
5323 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5324 g_snprintf(str, 64, tmp_str, removed);
5325 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5326
5327 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5328}
5329
eb9fd106
RN
5330/**
5331 * Reverse a track
5332 */
5333static void trw_layer_reverse ( gpointer pass_along[6] )
5334{
5335 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
0d2b891f
RN
5336 VikTrack *track;
5337 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5338 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5339 else
5340 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5341
5342 if ( ! track )
5343 return;
eb9fd106
RN
5344
5345 // Check valid track
5346 GList *trps = track->trackpoints;
5347 if ( !trps )
5348 return;
5349
5350 vik_track_reverse ( track );
5351
5352 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
5353}
5354
20b671c3
RN
5355/**
5356 * Similar to trw_layer_enum_item, but this uses a sorted method
5357 */
ce4bd1cf 5358/* Currently unused
20b671c3
RN
5359static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5360{
5361 GList **list = (GList**)udata;
ce4bd1cf 5362 // *list = g_list_prepend(*all, key); //unsorted method
20b671c3
RN
5363 // Sort named list alphabetically
5364 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5365}
ce4bd1cf 5366*/
20b671c3 5367
c9570f86
RN
5368/**
5369 * Now Waypoint specific sort
5370 */
5371static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5372{
5373 GList **list = (GList**)udata;
5374 // Sort named list alphabetically
5375 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5376}
5377
ce4bd1cf
RN
5378/**
5379 * Track specific sort
5380 */
5381static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5382{
5383 GList **list = (GList**)udata;
5384 // Sort named list alphabetically
5385 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5386}
5387
3d2ad4f8
RN
5388
5389typedef struct {
5390 gboolean has_same_track_name;
5391 const gchar *same_track_name;
5392} same_track_name_udata;
5393
5394static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5395{
5396 const gchar* namea = (const gchar*) aa;
5397 const gchar* nameb = (const gchar*) bb;
5398
5399 // the test
5400 gint result = strcmp ( namea, nameb );
5401
5402 if ( result == 0 ) {
5403 // Found two names the same
5404 same_track_name_udata *user_data = udata;
5405 user_data->has_same_track_name = TRUE;
5406 user_data->same_track_name = namea;
5407 }
5408
5409 // Leave ordering the same
5410 return 0;
5411}
5412
5413/**
0d2b891f 5414 * Find out if any tracks have the same name in this hash table
3d2ad4f8 5415 */
0d2b891f 5416static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
3d2ad4f8
RN
5417{
5418 // Sort items by name, then compare if any next to each other are the same
5419
5420 GList *track_names = NULL;
0d2b891f 5421 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
5422
5423 // No tracks
5424 if ( ! track_names )
5425 return FALSE;
5426
5427 same_track_name_udata udata;
5428 udata.has_same_track_name = FALSE;
5429
5430 // Use sort routine to traverse list comparing items
5431 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5432 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5433 // Still no tracks...
5434 if ( ! dummy_list )
5435 return FALSE;
5436
5437 return udata.has_same_track_name;
5438}
5439
5440/**
0d2b891f 5441 * Force unqiue track names for the track table specified
3d2ad4f8 5442 * Note the panel is a required parameter to enable the update of the names displayed
0d2b891f 5443 * Specify if on tracks or else on routes
3d2ad4f8 5444 */
0d2b891f 5445static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
3d2ad4f8
RN
5446{
5447 // . Search list for an instance of repeated name
5448 // . get track of this name
5449 // . create new name
5450 // . rename track & update equiv. treeview iter
5451 // . repeat until all different
5452
5453 same_track_name_udata udata;
5454
5455 GList *track_names = NULL;
5456 udata.has_same_track_name = FALSE;
5457 udata.same_track_name = NULL;
5458
0d2b891f 5459 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
5460
5461 // No tracks
5462 if ( ! track_names )
5463 return;
5464
5465 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5466
5467 // Still no tracks...
5468 if ( ! dummy_list1 )
5469 return;
5470
5471 while ( udata.has_same_track_name ) {
5472
5473 // Find a track with the same name
0d2b891f
RN
5474 VikTrack *trk;
5475 if ( ontrack )
5476 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5477 else
5478 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
3d2ad4f8
RN
5479
5480 if ( ! trk ) {
5481 // Broken :(
5482 g_critical("Houston, we've had a problem.");
5483 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5484 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5485 return;
5486 }
5487
5488 // Rename it
9748531a 5489 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
3d2ad4f8
RN
5490 vik_track_set_name ( trk, newname );
5491
5492 trku_udata udataU;
5493 udataU.trk = trk;
5494 udataU.uuid = NULL;
5495
5496 // Need want key of it for treeview update
0d2b891f 5497 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
3d2ad4f8
RN
5498
5499 if ( trkf && udataU.uuid ) {
5500
0d2b891f
RN
5501 GtkTreeIter *it;
5502 if ( ontrack )
5503 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5504 else
5505 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
3d2ad4f8
RN
5506
5507 if ( it ) {
5508 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5509#ifdef VIK_CONFIG_ALPHABETIZED_TRW
5510 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5511#endif
5512 }
5513 }
5514
5515 // Start trying to find same names again...
5516 track_names = NULL;
0d2b891f 5517 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
5518 udata.has_same_track_name = FALSE;
5519 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5520
5521 // No tracks any more - give up searching
5522 if ( ! dummy_list2 )
5523 udata.has_same_track_name = FALSE;
5524 }
5525
5526 // Update
5527 vik_layers_panel_emit_update ( vlp );
5528}
5529
20b671c3
RN
5530/**
5531 *
5532 */
5533static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5534{
5535 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5536 GList *all = NULL;
ce4bd1cf 5537
3d2ad4f8 5538 // Ensure list of track names offered is unique
0d2b891f 5539 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
3d2ad4f8
RN
5540 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5541 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
0d2b891f 5542 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
3d2ad4f8
RN
5543 }
5544 else
5545 return;
5546 }
ce4bd1cf 5547
20b671c3 5548 // Sort list alphabetically for better presentation
ce4bd1cf 5549 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
20b671c3
RN
5550
5551 if ( ! all ) {
5552 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5553 return;
5554 }
5555
5556 // Get list of items to delete from the user
5557 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5558 all,
5559 TRUE,
5560 _("Delete Selection"),
5561 _("Select tracks to delete"));
5562 g_list_free(all);
5563
5564 // Delete requested tracks
5565 // since specificly requested, IMHO no need for extra confirmation
5566 if ( delete_list ) {
5567 GList *l;
5568 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3d2ad4f8 5569 // This deletes first trk it finds of that name (but uniqueness is enforced above)
0d2b891f
RN
5570 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5571 }
5572 g_list_free(delete_list);
5573 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5574 }
5575}
5576
5577/**
5578 *
5579 */
5580static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5581{
5582 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5583 GList *all = NULL;
5584
5585 // Ensure list of track names offered is unique
5586 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5587 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5588 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5589 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5590 }
5591 else
5592 return;
5593 }
5594
5595 // Sort list alphabetically for better presentation
5596 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5597
5598 if ( ! all ) {
5599 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5600 return;
5601 }
5602
5603 // Get list of items to delete from the user
5604 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5605 all,
5606 TRUE,
5607 _("Delete Selection"),
5608 _("Select routes to delete") );
5609 g_list_free(all);
5610
5611 // Delete requested routes
5612 // since specificly requested, IMHO no need for extra confirmation
5613 if ( delete_list ) {
5614 GList *l;
5615 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5616 // This deletes first route it finds of that name (but uniqueness is enforced above)
5617 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
20b671c3
RN
5618 }
5619 g_list_free(delete_list);
f01eebd4 5620 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
20b671c3
RN
5621 }
5622}
5623
33a89785
RN
5624typedef struct {
5625 gboolean has_same_waypoint_name;
5626 const gchar *same_waypoint_name;
5627} same_waypoint_name_udata;
5628
5629static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5630{
5631 const gchar* namea = (const gchar*) aa;
5632 const gchar* nameb = (const gchar*) bb;
5633
5634 // the test
5635 gint result = strcmp ( namea, nameb );
5636
5637 if ( result == 0 ) {
5638 // Found two names the same
5639 same_waypoint_name_udata *user_data = udata;
5640 user_data->has_same_waypoint_name = TRUE;
5641 user_data->same_waypoint_name = namea;
5642 }
5643
5644 // Leave ordering the same
5645 return 0;
5646}
5647
5648/**
5649 * Find out if any waypoints have the same name in this layer
5650 */
5651gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5652{
5653 // Sort items by name, then compare if any next to each other are the same
5654
5655 GList *waypoint_names = NULL;
5656 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5657
5658 // No waypoints
5659 if ( ! waypoint_names )
5660 return FALSE;
5661
5662 same_waypoint_name_udata udata;
5663 udata.has_same_waypoint_name = FALSE;
5664
5665 // Use sort routine to traverse list comparing items
5666 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5667 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5668 // Still no waypoints...
5669 if ( ! dummy_list )
5670 return FALSE;
5671
5672 return udata.has_same_waypoint_name;
5673}
5674
5675/**
5676 * Force unqiue waypoint names for this layer
5677 * Note the panel is a required parameter to enable the update of the names displayed
5678 */
5679static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5680{
5681 // . Search list for an instance of repeated name
5682 // . get waypoint of this name
5683 // . create new name
5684 // . rename waypoint & update equiv. treeview iter
5685 // . repeat until all different
5686
5687 same_waypoint_name_udata udata;
5688
5689 GList *waypoint_names = NULL;
5690 udata.has_same_waypoint_name = FALSE;
5691 udata.same_waypoint_name = NULL;
5692
5693 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5694
5695 // No waypoints
5696 if ( ! waypoint_names )
5697 return;
5698
5699 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5700
5701 // Still no waypoints...
5702 if ( ! dummy_list1 )
5703 return;
5704
5705 while ( udata.has_same_waypoint_name ) {
5706
5707 // Find a waypoint with the same name
5708 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5709
5710 if ( ! waypoint ) {
5711 // Broken :(
5712 g_critical("Houston, we've had a problem.");
5713 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5714 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5715 return;
5716 }
5717
5718 // Rename it
9748531a 5719 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
33a89785
RN
5720 vik_waypoint_set_name ( waypoint, newname );
5721
5722 wpu_udata udataU;
5723 udataU.wp = waypoint;
5724 udataU.uuid = NULL;
5725
5726 // Need want key of it for treeview update
5727 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5728
5729 if ( wpf && udataU.uuid ) {
5730
5731 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5732
5733 if ( it ) {
5734 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5735#ifdef VIK_CONFIG_ALPHABETIZED_TRW
5736 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5737#endif
5738 }
5739 }
5740
5741 // Start trying to find same names again...
5742 waypoint_names = NULL;
5743 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5744 udata.has_same_waypoint_name = FALSE;
5745 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5746
5747 // No waypoints any more - give up searching
5748 if ( ! dummy_list2 )
5749 udata.has_same_waypoint_name = FALSE;
5750 }
5751
5752 // Update
5753 vik_layers_panel_emit_update ( vlp );
5754}
5755
20b671c3
RN
5756/**
5757 *
5758 */
5759static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5760{
5761 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5762 GList *all = NULL;
5763
33a89785
RN
5764 // Ensure list of waypoint names offered is unique
5765 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5766 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5767 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5768 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5769 }
5770 else
5771 return;
5772 }
c9570f86 5773
20b671c3 5774 // Sort list alphabetically for better presentation
c9570f86 5775 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
20b671c3
RN
5776 if ( ! all ) {
5777 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5778 return;
5779 }
5780
5781 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5782
5783 // Get list of items to delete from the user
5784 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5785 all,
5786 TRUE,
5787 _("Delete Selection"),
5788 _("Select waypoints to delete"));
5789 g_list_free(all);
5790
5791 // Delete requested waypoints
5792 // since specificly requested, IMHO no need for extra confirmation
5793 if ( delete_list ) {
5794 GList *l;
5795 for (l = delete_list; l != NULL; l = g_list_next(l)) {
33a89785 5796 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
c9570f86 5797 trw_layer_delete_waypoint_by_name (vtl, l->data);
20b671c3
RN
5798 }
5799 g_list_free(delete_list);
f01eebd4 5800 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
20b671c3
RN
5801 }
5802
5803}
111fa174 5804
6bb72350 5805static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
50a14534
EB
5806{
5807 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5808 if ( wp )
6bb72350 5809 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
50a14534
EB
5810}
5811
6bb72350 5812static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
50a14534 5813{
d64e2724
RN
5814 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5815 if ( !wp )
5816 return;
5817 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7d02a0b0 5818 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
50a14534
EB
5819 g_free ( webpage );
5820}
5821
a7cd93ac 5822static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
50a14534
EB
5823{
5824 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5825 {
c9570f86 5826 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
50a14534 5827
c9570f86
RN
5828 // No actual change to the name supplied
5829 if (strcmp(newname, wp->name) == 0 )
50a14534
EB
5830 return NULL;
5831
c9570f86
RN
5832 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5833
5834 if ( wpf ) {
5835 // An existing waypoint has been found with the requested name
5836 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 5837 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
c9570f86 5838 newname ) )
8499a412 5839 return NULL;
50a14534
EB
5840 }
5841
c9570f86
RN
5842 // Update WP name and refresh the treeview
5843 vik_waypoint_set_name (wp, newname);
50a14534 5844
50a14534 5845#ifdef VIK_CONFIG_ALPHABETIZED_TRW
c9570f86 5846 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
50a14534
EB
5847#endif
5848
5849 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
c9570f86
RN
5850
5851 return newname;
50a14534 5852 }
c9570f86 5853
50a14534
EB
5854 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5855 {
ce4bd1cf 5856 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
50a14534 5857
ce4bd1cf
RN
5858 // No actual change to the name supplied
5859 if (strcmp(newname, trk->name) == 0)
50a14534
EB
5860 return NULL;
5861
ce4bd1cf
RN
5862 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5863
5864 if ( trkf ) {
5865 // An existing track has been found with the requested name
5866 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 5867 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
ce4bd1cf 5868 newname ) )
8499a412 5869 return NULL;
50a14534 5870 }
ce4bd1cf
RN
5871 // Update track name and refresh GUI parts
5872 vik_track_set_name (trk, newname);
50a14534 5873
ce4bd1cf
RN
5874 // Update any subwindows that could be displaying this track which has changed name
5875 // Only one Track Edit Window
5876 if ( l->current_tp_track == trk && l->tpwin ) {
5877 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
50a14534 5878 }
ce4bd1cf
RN
5879 // Property Dialog of the track
5880 vik_trw_layer_propwin_update ( trk );
50a14534 5881
0d2b891f
RN
5882#ifdef VIK_CONFIG_ALPHABETIZED_TRW
5883 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5884#endif
5885
5886 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5887
5888 return newname;
5889 }
5890
5891 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5892 {
5893 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5894
5895 // No actual change to the name supplied
5896 if (strcmp(newname, trk->name) == 0)
5897 return NULL;
5898
5899 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5900
5901 if ( trkf ) {
5902 // An existing track has been found with the requested name
5903 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5904 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5905 newname ) )
5906 return NULL;
5907 }
5908 // Update track name and refresh GUI parts
5909 vik_track_set_name (trk, newname);
5910
5911 // Update any subwindows that could be displaying this track which has changed name
5912 // Only one Track Edit Window
5913 if ( l->current_tp_track == trk && l->tpwin ) {
5914 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5915 }
5916 // Property Dialog of the track
5917 vik_trw_layer_propwin_update ( trk );
5918
50a14534 5919#ifdef VIK_CONFIG_ALPHABETIZED_TRW
ce4bd1cf 5920 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
50a14534
EB
5921#endif
5922
5923 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
ce4bd1cf
RN
5924
5925 return newname;
50a14534
EB
5926 }
5927 return NULL;
5928}
5929
5930static gboolean is_valid_geocache_name ( gchar *str )
5931{
5932 gint len = strlen ( str );
0c1044e9 5933 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
5934}
5935
6bb72350 5936static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
28c82d8b 5937{
ce4bd1cf
RN
5938 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5939 a_acquire_set_filter_track ( trk );
28c82d8b
EB
5940}
5941
55340efa 5942#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 5943static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
bddd2056 5944{
008e972c 5945 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
bddd2056
EB
5946 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5947}
5948
008e972c 5949static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
bddd2056 5950{
008e972c 5951 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
bddd2056
EB
5952 if ( tr ) {
5953 gchar *escaped = uri_escape ( tr->comment );
5954 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7d02a0b0 5955 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
bddd2056 5956 g_free ( escaped );
bddd2056
EB
5957 g_free ( webpage );
5958 }
5959}
ff1f2b3e 5960#endif
bddd2056 5961
50eadc64
RN
5962/* vlp can be NULL if necessary - i.e. right-click from a tool */
5963/* viewpoint is now available instead */
a7cd93ac 5964static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534 5965{
ce4bd1cf 5966 static gpointer pass_along[8];
50a14534
EB
5967 GtkWidget *item;
5968 gboolean rv = FALSE;
5969
5970 pass_along[0] = l;
5971 pass_along[1] = vlp;
dc2c040e 5972 pass_along[2] = GINT_TO_POINTER (subtype);
50a14534 5973 pass_along[3] = sublayer;
169acf64 5974 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6bb72350 5975 pass_along[5] = vvp;
c9570f86 5976 pass_along[6] = iter;
ce4bd1cf 5977 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
50a14534 5978
0d2b891f 5979 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
5980 {
5981 rv = TRUE;
5982
5983 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5985 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5986 gtk_widget_show ( item );
5987
21700912 5988 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
0d2b891f
RN
5989 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
5990 if (tr && tr->property_dialog)
5991 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5992 }
5993 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
5994 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
21700912
QT
5995 if (tr && tr->property_dialog)
5996 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5997 }
5998
2cebc318
QT
5999 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6001 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6002 gtk_widget_show ( item );
6003
6004 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6006 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6007 gtk_widget_show ( item );
6008
50a14534
EB
6009 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6011 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6012 gtk_widget_show ( item );
6013
6014 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6015 {
5ede6aa6
RN
6016 gboolean separator_created = FALSE;
6017
50a14534
EB
6018 /* could be a right-click using the tool */
6019 if ( vlp != NULL ) {
5ede6aa6
RN
6020 item = gtk_menu_item_new ();
6021 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6022 gtk_widget_show ( item );
6023
6024 separator_created = TRUE;
6025
d6de71f9
RN
6026 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
6028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6029 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6030 gtk_widget_show ( item );
6031 }
6032
c9570f86 6033 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5ede6aa6 6034
c9570f86
RN
6035 if ( wp && wp->name ) {
6036 if ( is_valid_geocache_name ( wp->name ) ) {
50a14534 6037
c9570f86
RN
6038 if ( !separator_created ) {
6039 item = gtk_menu_item_new ();
6040 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6041 gtk_widget_show ( item );
6042 separator_created = TRUE;
6043 }
6044
6045 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6046 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6047 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6048 gtk_widget_show ( item );
6049 }
6050 }
a412f3f5
RN
6051
6052 if ( wp && wp->image )
6053 {
6054 if ( !separator_created ) {
6055 item = gtk_menu_item_new ();
6056 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6057 gtk_widget_show ( item );
6058 separator_created = TRUE;
6059 }
6060
6061 // Set up image paramater
6062 pass_along[5] = wp->image;
6063
d6de71f9
RN
6064 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6065 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
6066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6067 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6068 gtk_widget_show ( item );
b3eb3b98
RN
6069
6070#ifdef VIK_CONFIG_GEOTAG
6071 GtkWidget *geotag_submenu = gtk_menu_new ();
6072 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6073 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6074 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6075 gtk_widget_show ( item );
6076 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6077
6078 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6080 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6081 gtk_widget_show ( item );
6082
6083 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6085 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6086 gtk_widget_show ( item );
6087#endif
a412f3f5
RN
6088 }
6089
50a14534
EB
6090 }
6091 }
6092
0d2b891f 6093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d80642e
RN
6094 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6096 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6097 gtk_widget_show ( item );
6098 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6099 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6100 gtk_widget_set_sensitive ( item, TRUE );
6101 else
6102 gtk_widget_set_sensitive ( item, FALSE );
6103
6104 // Add separator
6105 item = gtk_menu_item_new ();
6106 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6107 gtk_widget_show ( item );
6108 }
6109
5ede6aa6
RN
6110 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6111 {
b66bb4ab 6112 rv = TRUE;
d6de71f9
RN
6113 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5ede6aa6
RN
6115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6116 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6117 gtk_widget_show ( item );
6118 }
6119
539ba038
RN
6120 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6121 {
d6de71f9
RN
6122 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6123 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
539ba038
RN
6124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6125 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6126 gtk_widget_show ( item );
6127
d6de71f9
RN
6128 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6129 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
539ba038
RN
6130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6131 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6132 gtk_widget_show ( item );
c9a5cbf9 6133
d6de71f9
RN
6134 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
6136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6137 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6138 gtk_widget_show ( item );
20b671c3 6139
d6de71f9
RN
6140 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
6142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6143 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6144 gtk_widget_show ( item );
539ba038
RN
6145 }
6146
f1e68516
RN
6147 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6148 {
6149 rv = TRUE;
6150
0d2b891f 6151 if ( l->current_track && !l->current_track->is_route ) {
37615c52
RN
6152 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6154 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6155 gtk_widget_show ( item );
6156 // Add separator
6157 item = gtk_menu_item_new ();
6158 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6159 gtk_widget_show ( item );
6160 }
6161
d6de71f9
RN
6162 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
f1e68516
RN
6164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6165 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6166 gtk_widget_show ( item );
c9a5cbf9 6167
37615c52
RN
6168 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6171 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6172 gtk_widget_show ( item );
6173 // Make it available only when a new track *not* already in progress
6174 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6175
d6de71f9
RN
6176 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
6178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6179 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6180 gtk_widget_show ( item );
20b671c3 6181
d6de71f9
RN
6182 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6183 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
6184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6185 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6186 gtk_widget_show ( item );
f1e68516
RN
6187 }
6188
0d2b891f
RN
6189 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6190 {
6191 rv = TRUE;
6192
6193 if ( l->current_track && l->current_track->is_route ) {
6194 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6195 // Reuse finish track method
6196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6197 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6198 gtk_widget_show ( item );
6199 // Add separator
6200 item = gtk_menu_item_new ();
6201 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6202 gtk_widget_show ( item );
6203 }
6204
6205 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6208 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6209 gtk_widget_show ( item );
6210
6211 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6212 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6214 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6215 gtk_widget_show ( item );
6216 // Make it available only when a new track *not* already in progress
6217 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6218
6219 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6222 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6223 gtk_widget_show ( item );
6224
6225 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6228 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6229 gtk_widget_show ( item );
6230 }
6231
6232 GtkWidget *upload_submenu = gtk_menu_new ();
6233
6234 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
6235 {
6236 item = gtk_menu_item_new ();
6237 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6238 gtk_widget_show ( item );
6239
0d2b891f 6240 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
37615c52 6241 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
0d2b891f
RN
6242 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6243 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6244 if ( l->current_track ) {
37615c52
RN
6245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6246 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6247 gtk_widget_show ( item );
6248
6249 // Add separator
6250 item = gtk_menu_item_new ();
6251 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6252 gtk_widget_show ( item );
6253 }
6254
0d2b891f
RN
6255 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6256 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6257 else
6258 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
88a49424
RN
6259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6262 gtk_widget_show ( item );
6263
6264 GtkWidget *goto_submenu;
937b36ed 6265 goto_submenu = gtk_menu_new ();
d6de71f9
RN
6266 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6267 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
937b36ed
RN
6268 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6269 gtk_widget_show ( item );
6270 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6271
d6de71f9
RN
6272 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6273 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
50a14534 6274 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 6275 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
6276 gtk_widget_show ( item );
6277
d6de71f9
RN
6278 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6279 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534 6280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 6281 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
6282 gtk_widget_show ( item );
6283
d6de71f9
RN
6284 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
50a14534 6286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 6287 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 6288 gtk_widget_show ( item );
111fa174 6289
d6de71f9
RN
6290 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6291 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
c28faca8
RN
6292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6293 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6294 gtk_widget_show ( item );
6295
d6de71f9
RN
6296 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
c28faca8
RN
6298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6299 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6300 gtk_widget_show ( item );
6301
0d2b891f
RN
6302 // Routes don't have speeds
6303 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6304 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6307 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6308 gtk_widget_show ( item );
6309 }
03e7da75 6310
59f9414d
RN
6311 GtkWidget *combine_submenu;
6312 combine_submenu = gtk_menu_new ();
6313 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6314 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6315 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
469113fb 6316 gtk_widget_show ( item );
59f9414d 6317 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
469113fb 6318
0d2b891f
RN
6319 // Routes don't have times or segments...
6320 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6321 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6323 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6324 gtk_widget_show ( item );
6325
6326 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6328 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6329 gtk_widget_show ( item );
6330 }
111fa174 6331
40a68e7c 6332 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab 6333 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
59f9414d 6334 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
291edcab
HR
6335 gtk_widget_show ( item );
6336
0d2b891f
RN
6337 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6338 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6339 else
6340 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
9c34f614
RN
6341 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6342 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6343 gtk_widget_show ( item );
6344
6b5b6c47
RN
6345 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6346 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6347 else
6348 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6350 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6351 gtk_widget_show ( item );
6352
78ac928c
RN
6353 GtkWidget *split_submenu;
6354 split_submenu = gtk_menu_new ();
6355 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6356 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6357 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
291edcab 6358 gtk_widget_show ( item );
78ac928c 6359 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
291edcab 6360
0d2b891f
RN
6361 // Routes don't have times or segments...
6362 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6363 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6365 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6366 gtk_widget_show ( item );
6367
6368 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6369 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6370 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6371 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6372 gtk_widget_show ( item );
6373 }
7114e879 6374
7306a492 6375 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
af2341f3 6376 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
78ac928c 6377 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
af2341f3
RN
6378 gtk_widget_show ( item );
6379
a4aefe69
RN
6380 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6381 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6382 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6383 gtk_widget_show ( item );
6384 // Make it available only when a trackpoint is selected.
6385 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6386
b6eda120
RN
6387 GtkWidget *delete_submenu;
6388 delete_submenu = gtk_menu_new ();
6389 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6390 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6391 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6392 gtk_widget_show ( item );
6393 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6394
6395 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6397 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6398 gtk_widget_show ( item );
6399
6579ca1f
RN
6400 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6401 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6402 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
af2341f3
RN
6403 gtk_widget_show ( item );
6404
0d2b891f
RN
6405 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6406 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6407 else
6408 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
eb9fd106
RN
6409 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6411 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6412 gtk_widget_show ( item );
6413
6bb72350
RN
6414 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6415 if ( vlp ) {
0d2b891f
RN
6416 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6417 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6418 else
6419 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
d6de71f9 6420 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
6421 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6422 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6423 gtk_widget_show ( item );
6424 }
ad0a8c2d 6425
d6de71f9
RN
6426 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6427 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
6428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6429 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8fb71d6c
EB
6430 gtk_widget_show ( item );
6431
0d2b891f
RN
6432 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6433 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6434 else
6435 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
d6de71f9 6436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7f6757c4
RN
6437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6438 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6439 gtk_widget_show ( item );
6440
0d2b891f
RN
6441 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6442 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6443 else
6444 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
d6de71f9 6445 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8fb71d6c
EB
6446 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6447 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 6448 gtk_widget_show ( item );
5092de80 6449
2f5d7ea1
RN
6450 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6451 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6452 else
6453 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6454 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6456 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6457 gtk_widget_show ( item );
6458
55340efa 6459#ifdef VIK_CONFIG_GOOGLE
008e972c
RN
6460 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6461 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6462 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
6463 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6464 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6465 gtk_widget_show ( item );
6466 }
ebf1bd39 6467#endif
a7955c1d 6468
0d2b891f
RN
6469 // ATM can't upload a single waypoint but can do waypoints to a GPS
6470 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6471 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6472 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6473 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6474 gtk_widget_show ( item );
6475 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6476
6477 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6478 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6480 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6481 gtk_widget_show ( item );
6482 }
6483 }
e50758c7 6484
008e972c
RN
6485#ifdef VIK_CONFIG_GOOGLE
6486 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
6487 {
6488 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6489 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6490 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
6491 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6492 gtk_widget_show ( item );
6493 }
6494#endif
6495
0d2b891f
RN
6496 // Some things aren't usable with routes
6497 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
5092de80 6498#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9 6499 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
ce4bd1cf
RN
6500 // Convert internal pointer into actual track for usage outside this file
6501 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
d6de71f9 6502 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
e50758c7
RN
6503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6504 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5092de80
GB
6505 gtk_widget_show ( item );
6506#endif
bddd2056 6507
d6de71f9
RN
6508 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6509 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
28c82d8b
EB
6510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6511 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6512 gtk_widget_show ( item );
6513
6bb72350
RN
6514 /* ATM This function is only available via the layers panel, due to needing a vlp */
6515 if ( vlp ) {
6516 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
0d2b891f
RN
6517 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6518 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6bb72350 6519 if ( item ) {
0d2b891f
RN
6520 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6521 gtk_widget_show ( item );
6bb72350
RN
6522 }
6523 }
c95d6b00 6524
b3eb3b98 6525#ifdef VIK_CONFIG_GEOTAG
0d2b891f
RN
6526 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6527 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6528 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6529 gtk_widget_show ( item );
b3eb3b98 6530#endif
0d2b891f 6531 }
b3eb3b98 6532
0d2b891f 6533 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
c95d6b00
RN
6534 // Only show on viewport popmenu when a trackpoint is selected
6535 if ( ! vlp && l->current_tpl ) {
6536 // Add separator
6537 item = gtk_menu_item_new ();
6538 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6539 gtk_widget_show ( item );
6540
6541 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6544 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6545 gtk_widget_show ( item );
6546 }
50a14534
EB
6547 }
6548
50a14534
EB
6549 return rv;
6550}
6551
db79f75f
RN
6552static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6553{
6554 /* sanity checks */
6555 if (!vtl->current_tpl)
6556 return;
6557 if (!vtl->current_tpl->next)
6558 return;
6559
6560 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6561 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6562
6563 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6564 if ( tp_next ) {
6565
6566 VikTrackpoint *tp_new = vik_trackpoint_new();
6567 struct LatLon ll_current, ll_next;
6568 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6569 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6570
6571 /* main positional interpolation */
6572 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6573 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6574
6575 /* Now other properties that can be interpolated */
6576 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6577
6578 if (tp_current->has_timestamp && tp_next->has_timestamp) {
6579 /* Note here the division is applied to each part, then added
6580 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6581 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6582 tp_new->has_timestamp = TRUE;
6583 }
6584
6585 if (tp_current->speed != NAN && tp_next->speed != NAN)
6586 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6587
6588 /* TODO - improve interpolation of course, as it may not be correct.
6589 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6590 [similar applies if value is in radians] */
6591 if (tp_current->course != NAN && tp_next->course != NAN)
6592 tp_new->speed = (tp_current->course + tp_next->course)/2;
6593
6594 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6595
6596 /* Insert new point into the trackpoints list after the current TP */
0d2b891f
RN
6597 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6598 if ( !trk )
6599 // Otherwise try routes
6600 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6601 if ( !trk )
6602 return;
6603
6604 gint index = g_list_index ( trk->trackpoints, tp_current );
db79f75f 6605 if ( index > -1 ) {
0d2b891f 6606 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
db79f75f
RN
6607 }
6608 }
6609}
50a14534 6610
50a14534
EB
6611static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6612{
6613 if ( vtl->tpwin )
6614 {
6615 if ( destroy)
6616 {
6617 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6618 vtl->tpwin = NULL;
6619 }
6620 else
6621 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6622 }
6623 if ( vtl->current_tpl )
6624 {
6625 vtl->current_tpl = NULL;
ce4bd1cf
RN
6626 vtl->current_tp_track = NULL;
6627 vtl->current_tp_id = NULL;
f01eebd4 6628 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534
EB
6629 }
6630}
6631
6632static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6633{
6634 g_assert ( vtl->tpwin != NULL );
6635 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6636 trw_layer_cancel_current_tp ( vtl, TRUE );
0d601fd4
RN
6637
6638 if ( vtl->current_tpl == NULL )
6639 return;
6640
6641 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
50a14534 6642 {
0d2b891f 6643 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
84d3d9f9 6644 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
6645 }
6646 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6647 {
ce4bd1cf 6648 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
0d2b891f
RN
6649 if ( tr == NULL )
6650 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
ce4bd1cf
RN
6651 if ( tr == NULL )
6652 return;
50a14534 6653
ce4bd1cf 6654 GList *new_tpl;
50a14534 6655
ab4553c3 6656 // Find available adjacent trackpoint
50a14534
EB
6657 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6658 {
6659 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6660 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6661
ab4553c3
RN
6662 // Delete current trackpoint
6663 vik_trackpoint_free ( vtl->current_tpl->data );
6664 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
50a14534 6665
ab4553c3
RN
6666 // Set to current to the available adjacent trackpoint
6667 vtl->current_tpl = new_tpl;
6668
6669 // Reset dialog with the available adjacent trackpoint
ce4bd1cf
RN
6670 if ( vtl->current_tp_track )
6671 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
50a14534 6672
f01eebd4 6673 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534
EB
6674 }
6675 else
6676 {
ab4553c3
RN
6677 // Delete current trackpoint
6678 vik_trackpoint_free ( vtl->current_tpl->data );
6679 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
50a14534
EB
6680 trw_layer_cancel_current_tp ( vtl, FALSE );
6681 }
6682 }
6683 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6684 {
ce4bd1cf
RN
6685 if ( vtl->current_tp_track )
6686 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
f01eebd4 6687 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
50a14534
EB
6688 }
6689 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6690 {
ce4bd1cf
RN
6691 if ( vtl->current_tp_track )
6692 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
f01eebd4 6693 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534 6694 }
2880a1de
RN
6695 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6696 {
6697 trw_layer_insert_tp_after_current_tp ( vtl );
f01eebd4 6698 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
2880a1de 6699 }
50a14534 6700 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
f01eebd4 6701 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534
EB
6702}
6703
6704static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6705{
6706 if ( ! vtl->tpwin )
6707 {
6708 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6709 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6710 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6711 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6712 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6713 }
6714 if ( vtl->current_tpl )
ce4bd1cf
RN
6715 if ( vtl->current_tp_track )
6716 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
6717 /* set layer name and TP data */
6718}
6719
941aa6e9
AF
6720/***************************************************************************
6721 ** Tool code
6722 ***************************************************************************/
50a14534 6723
941aa6e9 6724/*** Utility data structures and functions ****/
50a14534
EB
6725
6726typedef struct {
6727 gint x, y;
6728 gint closest_x, closest_y;
c9570f86 6729 gpointer *closest_wp_id;
50a14534
EB
6730 VikWaypoint *closest_wp;
6731 VikViewport *vvp;
6732} WPSearchParams;
6733
941aa6e9
AF
6734typedef struct {
6735 gint x, y;
6736 gint closest_x, closest_y;
ce4bd1cf 6737 gpointer closest_track_id;
941aa6e9
AF
6738 VikTrackpoint *closest_tp;
6739 VikViewport *vvp;
6740 GList *closest_tpl;
6741} TPSearchParams;
6742
c9570f86 6743static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
50a14534
EB
6744{
6745 gint x, y;
6746 if ( !wp->visible )
6747 return;
6748
6749 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
6750
6751 // If waypoint has an image then use the image size to select
6752 if ( wp->image ) {
6753 gint slackx, slacky;
6754 slackx = wp->image_width / 2;
6755 slacky = wp->image_height / 2;
6756
6757 if ( x <= params->x + slackx && x >= params->x - slackx
6758 && y <= params->y + slacky && y >= params->y - slacky ) {
c9570f86 6759 params->closest_wp_id = id;
59b0a97a
RN
6760 params->closest_wp = wp;
6761 params->closest_x = x;
6762 params->closest_y = y;
6763 }
50a14534 6764 }
59b0a97a
RN
6765 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6766 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
6767 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6768 {
c9570f86 6769 params->closest_wp_id = id;
59b0a97a
RN
6770 params->closest_wp = wp;
6771 params->closest_x = x;
6772 params->closest_y = y;
6773 }
50a14534
EB
6774}
6775
ce4bd1cf 6776static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
50a14534 6777{
941aa6e9
AF
6778 GList *tpl = t->trackpoints;
6779 VikTrackpoint *tp;
50a14534 6780
941aa6e9
AF
6781 if ( !t->visible )
6782 return;
50a14534 6783
941aa6e9
AF
6784 while (tpl)
6785 {
6786 gint x, y;
6787 tp = VIK_TRACKPOINT(tpl->data);
6788
6789 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6790
6791 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6792 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
6793 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 6794 {
ce4bd1cf 6795 params->closest_track_id = id;
941aa6e9
AF
6796 params->closest_tp = tp;
6797 params->closest_tpl = tpl;
6798 params->closest_x = x;
6799 params->closest_y = y;
50a14534 6800 }
941aa6e9 6801 tpl = tpl->next;
50a14534 6802 }
941aa6e9
AF
6803}
6804
0d2b891f
RN
6805// ATM: Leave this as 'Track' only.
6806// Not overly bothered about having a snap to route trackpoint capability
941aa6e9
AF
6807static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6808{
6809 TPSearchParams params;
6810 params.x = x;
6811 params.y = y;
6812 params.vvp = vvp;
ce4bd1cf 6813 params.closest_track_id = NULL;
941aa6e9
AF
6814 params.closest_tp = NULL;
6815 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
6816 return params.closest_tp;
50a14534
EB
6817}
6818
6819static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6820{
6821 WPSearchParams params;
6822 params.x = x;
6823 params.y = y;
6824 params.vvp = vvp;
6825 params.closest_wp = NULL;
c9570f86 6826 params.closest_wp_id = NULL;
50a14534
EB
6827 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
6828 return params.closest_wp;
6829}
6830
95d1b757 6831
08f14055
RN
6832// Some forward declarations
6833static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6834static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6835static void marker_end_move ( tool_ed_t *t );
6836//
6837
6838static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6839{
6840 if ( t->holding ) {
6841 VikCoord new_coord;
6842 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6843
6844 // Here always allow snapping back to the original location
6845 // this is useful when one decides not to move the thing afterall
6846 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6847
6848 // snap to TP
6849 if ( event->state & GDK_CONTROL_MASK )
6850 {
6851 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6852 if ( tp )
6853 new_coord = tp->coord;
6854 }
6855
6856 // snap to WP
6857 if ( event->state & GDK_SHIFT_MASK )
6858 {
6859 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6860 if ( wp )
6861 new_coord = wp->coord;
6862 }
6863
6864 gint x, y;
6865 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6866
6867 marker_moveto ( t, x, y );
6868
6869 return TRUE;
6870 }
6871 return FALSE;
6872}
6873
6874static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6875{
6876 if ( t->holding && event->button == 1 )
6877 {
6878 VikCoord new_coord;
6879 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6880
6881 // snap to TP
6882 if ( event->state & GDK_CONTROL_MASK )
6883 {
6884 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6885 if ( tp )
6886 new_coord = tp->coord;
6887 }
6888
6889 // snap to WP
6890 if ( event->state & GDK_SHIFT_MASK )
6891 {
6892 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6893 if ( wp )
6894 new_coord = wp->coord;
6895 }
6896
6897 marker_end_move ( t );
6898
6899 // Determine if working on a waypoint or a trackpoint
6900 if ( t->is_waypoint )
6901 vtl->current_wp->coord = new_coord;
6902 else {
6903 if ( vtl->current_tpl ) {
ce4bd1cf 6904 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
08f14055
RN
6905
6906 if ( vtl->tpwin )
ce4bd1cf
RN
6907 if ( vtl->current_tp_track )
6908 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055
RN
6909 }
6910 }
6911
6912 // Reset
c9570f86
RN
6913 vtl->current_wp = NULL;
6914 vtl->current_wp_id = NULL;
08f14055
RN
6915 trw_layer_cancel_current_tp ( vtl, FALSE );
6916
f01eebd4 6917 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
08f14055
RN
6918 return TRUE;
6919 }
6920 return FALSE;
6921}
6922
77ad64fa
RN
6923/*
6924 Returns true if a waypoint or track is found near the requested event position for this particular layer
6925 The item found is automatically selected
6926 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6927 */
08f14055 6928static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
6929{
6930 if ( event->button != 1 )
6931 return FALSE;
6932
6933 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6934 return FALSE;
6935
0d2b891f 6936 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
77ad64fa
RN
6937 return FALSE;
6938
08f14055 6939 // 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
6940
6941 if (vtl->waypoints_visible) {
6942 WPSearchParams wp_params;
6943 wp_params.vvp = vvp;
6944 wp_params.x = event->x;
6945 wp_params.y = event->y;
c9570f86 6946 wp_params.closest_wp_id = NULL;
77ad64fa
RN
6947 wp_params.closest_wp = NULL;
6948
6949 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
6950
6951 if ( wp_params.closest_wp ) {
08f14055
RN
6952
6953 // Select
c9570f86 6954 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
08f14055
RN
6955
6956 // Too easy to move it so must be holding shift to start immediately moving it
3c73e6c4 6957 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
08f14055 6958 if ( event->state & GDK_SHIFT_MASK ||
3c73e6c4 6959 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
08f14055
RN
6960 // Put into 'move buffer'
6961 // NB vvp & vw already set in tet
6962 tet->vtl = (gpointer)vtl;
6963 tet->is_waypoint = TRUE;
6964
6965 marker_begin_move (tet, event->x, event->y);
6966 }
6967
c9570f86
RN
6968 vtl->current_wp = wp_params.closest_wp;
6969 vtl->current_wp_id = wp_params.closest_wp_id;
08f14055 6970
f01eebd4 6971 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
08f14055 6972
77ad64fa
RN
6973 return TRUE;
6974 }
6975 }
6976
0d2b891f
RN
6977 // Used for both track and route lists
6978 TPSearchParams tp_params;
6979 tp_params.vvp = vvp;
6980 tp_params.x = event->x;
6981 tp_params.y = event->y;
6982 tp_params.closest_track_id = NULL;
6983 tp_params.closest_tp = NULL;
77ad64fa 6984
0d2b891f 6985 if (vtl->tracks_visible) {
77ad64fa
RN
6986 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
6987
6988 if ( tp_params.closest_tp ) {
08f14055
RN
6989
6990 // Always select + highlight the track
ce4bd1cf 6991 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
08f14055
RN
6992
6993 tet->is_waypoint = FALSE;
6994
6995 // Select the Trackpoint
6996 // Can move it immediately when control held or it's the previously selected tp
6997 if ( event->state & GDK_CONTROL_MASK ||
6998 vtl->current_tpl == tp_params.closest_tpl ) {
6999 // Put into 'move buffer'
7000 // NB vvp & vw already set in tet
7001 tet->vtl = (gpointer)vtl;
7002 marker_begin_move (tet, event->x, event->y);
7003 }
7004
7005 vtl->current_tpl = tp_params.closest_tpl;
ce4bd1cf
RN
7006 vtl->current_tp_id = tp_params.closest_track_id;
7007 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
08f14055 7008
95d1b757
RN
7009 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7010
08f14055 7011 if ( vtl->tpwin )
ce4bd1cf 7012 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055 7013
f01eebd4 7014 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
77ad64fa
RN
7015 return TRUE;
7016 }
7017 }
7018
0d2b891f
RN
7019 // Try again for routes
7020 if (vtl->routes_visible) {
7021 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7022
7023 if ( tp_params.closest_tp ) {
7024
7025 // Always select + highlight the track
7026 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7027
7028 tet->is_waypoint = FALSE;
7029
7030 // Select the Trackpoint
7031 // Can move it immediately when control held or it's the previously selected tp
7032 if ( event->state & GDK_CONTROL_MASK ||
7033 vtl->current_tpl == tp_params.closest_tpl ) {
7034 // Put into 'move buffer'
7035 // NB vvp & vw already set in tet
7036 tet->vtl = (gpointer)vtl;
7037 marker_begin_move (tet, event->x, event->y);
7038 }
7039
7040 vtl->current_tpl = tp_params.closest_tpl;
7041 vtl->current_tp_id = tp_params.closest_track_id;
7042 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7043
7044 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7045
7046 if ( vtl->tpwin )
7047 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7048
7049 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7050 return TRUE;
7051 }
7052 }
7053
77ad64fa 7054 /* these aren't the droids you're looking for */
c9570f86
RN
7055 vtl->current_wp = NULL;
7056 vtl->current_wp_id = NULL;
08f14055
RN
7057 trw_layer_cancel_current_tp ( vtl, FALSE );
7058
95d1b757
RN
7059 // Blank info
7060 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7061
77ad64fa
RN
7062 return FALSE;
7063}
7064
e46f259a
RN
7065static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7066{
7067 if ( event->button != 3 )
7068 return FALSE;
7069
7070 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7071 return FALSE;
7072
0d2b891f 7073 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
e46f259a
RN
7074 return FALSE;
7075
7076 /* Post menu for the currently selected item */
7077
7078 /* See if a track is selected */
7079 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7080 if ( track && track->visible ) {
7081
60a69560 7082 if ( track->name ) {
e46f259a
RN
7083
7084 if ( vtl->track_right_click_menu )
60a69560 7085 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
e46f259a
RN
7086
7087 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7088
60a69560
RN
7089 trku_udata udataU;
7090 udataU.trk = track;
7091 udataU.uuid = NULL;
7092
0d2b891f
RN
7093 gpointer *trkf;
7094 if ( track->is_route )
7095 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7096 else
7097 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
60a69560
RN
7098
7099 if ( trkf && udataU.uuid ) {
7100
0d2b891f
RN
7101 GtkTreeIter *iter;
7102 if ( track->is_route )
7103 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7104 else
7105 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
60a69560
RN
7106
7107 trw_layer_sublayer_add_menu_items ( vtl,
7108 vtl->track_right_click_menu,
7109 NULL,
0d2b891f 7110 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
60a69560
RN
7111 udataU.uuid,
7112 iter,
7113 vvp );
7114 }
e46f259a
RN
7115
7116 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7117
7118 return TRUE;
7119 }
7120 }
7121
7122 /* See if a waypoint is selected */
7123 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7124 if ( waypoint && waypoint->visible ) {
60a69560 7125 if ( waypoint->name ) {
e46f259a
RN
7126
7127 if ( vtl->wp_right_click_menu )
60a69560 7128 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
e46f259a
RN
7129
7130 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
60a69560
RN
7131
7132 wpu_udata udata;
7133 udata.wp = waypoint;
7134 udata.uuid = NULL;
7135
7136 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7137
7138 if ( wpf && udata.uuid ) {
7139 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7140
7141 trw_layer_sublayer_add_menu_items ( vtl,
7142 vtl->wp_right_click_menu,
7143 NULL,
7144 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7145 udata.uuid,
7146 iter,
7147 vvp );
7148 }
e46f259a
RN
7149 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7150
7151 return TRUE;
7152 }
7153 }
7154
7155 return FALSE;
7156}
7157
7432fddf
AF
7158/* background drawing hook, to be passed the viewport */
7159static gboolean tool_sync_done = TRUE;
7160
7161static gboolean tool_sync(gpointer data)
7162{
7163 VikViewport *vvp = data;
7164 gdk_threads_enter();
7165 vik_viewport_sync(vvp);
7166 tool_sync_done = TRUE;
7167 gdk_threads_leave();
7168 return FALSE;
7169}
7170
7432fddf
AF
7171static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7172{
7173 t->holding = TRUE;
7174 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7175 gdk_gc_set_function ( t->gc, GDK_INVERT );
7176 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7177 vik_viewport_sync(t->vvp);
7178 t->oldx = x;
7179 t->oldy = y;
7180}
7181
7182static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7183{
7184 VikViewport *vvp = t->vvp;
7185 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7186 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7187 t->oldx = x;
7188 t->oldy = y;
7b203521 7189
7432fddf
AF
7190 if (tool_sync_done) {
7191 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7192 tool_sync_done = FALSE;
7193 }
7194}
7195
7196static void marker_end_move ( tool_ed_t *t )
7197{
7198 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7199 g_object_unref ( t->gc );
7200 t->holding = FALSE;
7201}
7202
941aa6e9
AF
7203/*** Edit waypoint ****/
7204
7205static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7206{
7432fddf
AF
7207 tool_ed_t *t = g_new(tool_ed_t, 1);
7208 t->vvp = vvp;
7209 t->holding = FALSE;
7210 return t;
941aa6e9
AF
7211}
7212
7432fddf 7213static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
7214{
7215 WPSearchParams params;
7432fddf
AF
7216 tool_ed_t *t = data;
7217 VikViewport *vvp = t->vvp;
50a14534 7218
941aa6e9
AF
7219 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7220 return FALSE;
7432fddf
AF
7221
7222 if ( t->holding ) {
7223 return TRUE;
7224 }
7225
87741170
RN
7226 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7227 return FALSE;
7228
50a14534
EB
7229 if ( vtl->current_wp && vtl->current_wp->visible )
7230 {
7231 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7232 gint x, y;
7233 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7234
c75d78d7
AF
7235 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7236 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
7237 {
7238 if ( event->button == 3 )
7239 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
7240 else {
7241 marker_begin_move(t, event->x, event->y);
7242 }
50a14534
EB
7243 return TRUE;
7244 }
7245 }
7246
7247 params.vvp = vvp;
7248 params.x = event->x;
7249 params.y = event->y;
c9570f86 7250 params.closest_wp_id = NULL;
50a14534
EB
7251 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7252 params.closest_wp = NULL;
7253 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
7254 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7255 {
d84ade77 7256 // how do we get here?
7432fddf 7257 marker_begin_move(t, event->x, event->y);
7742da66 7258 g_critical("shouldn't be here");
d84ade77 7259 return FALSE;
50a14534
EB
7260 }
7261 else if ( params.closest_wp )
7262 {
7263 if ( event->button == 3 )
7264 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7265 else
7266 vtl->waypoint_rightclick = FALSE;
7267
c9570f86 7268 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8623d370 7269
50a14534 7270 vtl->current_wp = params.closest_wp;
c9570f86 7271 vtl->current_wp_id = params.closest_wp_id;
50a14534 7272
50a14534 7273 /* could make it so don't update if old WP is off screen and new is null but oh well */
f01eebd4 7274 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
7275 return TRUE;
7276 }
7277
7278 vtl->current_wp = NULL;
c9570f86 7279 vtl->current_wp_id = NULL;
50a14534 7280 vtl->waypoint_rightclick = FALSE;
f01eebd4 7281 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7432fddf
AF
7282 return FALSE;
7283}
7284
dc2c040e 7285static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
7286{
7287 tool_ed_t *t = data;
7288 VikViewport *vvp = t->vvp;
7289
7290 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7291 return FALSE;
7292
7293 if ( t->holding ) {
7294 VikCoord new_coord;
7295 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7296
7297 /* snap to TP */
7298 if ( event->state & GDK_CONTROL_MASK )
7299 {
7300 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7301 if ( tp )
7302 new_coord = tp->coord;
7303 }
7304
7305 /* snap to WP */
7306 if ( event->state & GDK_SHIFT_MASK )
7307 {
7308 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7309 if ( wp && wp != vtl->current_wp )
7310 new_coord = wp->coord;
7311 }
7312
7313 {
7314 gint x, y;
7315 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 7316
7432fddf
AF
7317 marker_moveto ( t, x, y );
7318 }
7319 return TRUE;
7320 }
50a14534
EB
7321 return FALSE;
7322}
7323
7432fddf 7324static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 7325{
7432fddf
AF
7326 tool_ed_t *t = data;
7327 VikViewport *vvp = t->vvp;
7328
941aa6e9
AF
7329 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7330 return FALSE;
7432fddf
AF
7331
7332 if ( t->holding && event->button == 1 )
941aa6e9
AF
7333 {
7334 VikCoord new_coord;
941aa6e9
AF
7335 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7336
7337 /* snap to TP */
7338 if ( event->state & GDK_CONTROL_MASK )
7339 {
7340 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7341 if ( tp )
7342 new_coord = tp->coord;
7343 }
7344
7345 /* snap to WP */
7346 if ( event->state & GDK_SHIFT_MASK )
7347 {
7348 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7349 if ( wp && wp != vtl->current_wp )
7350 new_coord = wp->coord;
7351 }
7352
7432fddf
AF
7353 marker_end_move ( t );
7354
941aa6e9 7355 vtl->current_wp->coord = new_coord;
f01eebd4 7356 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
7357 return TRUE;
7358 }
7359 /* PUT IN RIGHT PLACE!!! */
7432fddf 7360 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
7361 {
7362 if ( vtl->wp_right_click_menu )
4f14a010 7363 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
0b951ebe
RN
7364 if ( vtl->current_wp ) {
7365 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
c9570f86 7366 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
7367 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7368 }
941aa6e9
AF
7369 vtl->waypoint_rightclick = FALSE;
7370 }
7371 return FALSE;
7372}
7373
7374/*** New track ****/
7375
7376static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7377{
7378 return vvp;
7379}
7380
7b203521
EB
7381typedef struct {
7382 VikTrwLayer *vtl;
745fda83
RN
7383 GdkDrawable *drawable;
7384 GdkGC *gc;
7385 GdkPixmap *pixmap;
7386} draw_sync_t;
7b203521 7387
745fda83
RN
7388/*
7389 * Draw specified pixmap
7390 */
7391static gboolean draw_sync ( gpointer data )
7b203521 7392{
745fda83 7393 draw_sync_t *ds = (draw_sync_t*) data;
ef5e8132
RN
7394 // Sometimes don't want to draw
7395 // normally because another update has taken precedent such as panning the display
7396 // which means this pixmap is no longer valid
7397 if ( ds->vtl->draw_sync_do ) {
7398 gdk_threads_enter();
7399 gdk_draw_drawable (ds->drawable,
7400 ds->gc,
7401 ds->pixmap,
7402 0, 0, 0, 0, -1, -1);
7403 ds->vtl->draw_sync_done = TRUE;
7404 gdk_threads_leave();
7405 }
7b203521
EB
7406 return FALSE;
7407}
7408
19a0d58f 7409static gchar* distance_string (gdouble distance)
8da84040
RN
7410{
7411 gchar str[128];
7412
7413 /* draw label with distance */
7414 vik_units_distance_t dist_units = a_vik_get_units_distance ();
7415 switch (dist_units) {
7416 case VIK_UNITS_DISTANCE_MILES:
7417 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7418 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7419 } else if (distance < 1609.4) {
7420 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7421 } else {
7422 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7423 }
7424 break;
7425 default:
7426 // VIK_UNITS_DISTANCE_KILOMETRES
7427 if (distance >= 1000 && distance < 100000) {
7428 g_sprintf(str, "%3.2f km", distance/1000.0);
7429 } else if (distance < 1000) {
7430 g_sprintf(str, "%d m", (int)distance);
7431 } else {
7432 g_sprintf(str, "%d km", (int)distance/1000);
7433 }
7434 break;
7435 }
7436 return g_strdup (str);
7437}
7438
7439/*
7440 * Actually set the message in statusbar
7441 */
7442static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
7443{
7444 // Only show elevation data when track has some elevation properties
7445 gchar str_gain_loss[64];
7446 str_gain_loss[0] = '\0';
7447
7448 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7449 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7450 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7451 else
7452 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7453 }
7454
7455 // Write with full gain/loss information
7456 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
7457 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
7458 g_free ( msg );
7459}
7460
7461/*
7462 * Figure out what information should be set in the statusbar and then write it
7463 */
7464static void update_statusbar ( VikTrwLayer *vtl )
7465{
7466 // Get elevation data
7467 gdouble elev_gain, elev_loss;
7468 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7469
7470 /* Find out actual distance of current track */
7471 gdouble distance = vik_track_get_length (vtl->current_track);
19a0d58f 7472 gchar *str = distance_string (distance);
8da84040
RN
7473
7474 statusbar_write (str, elev_gain, elev_loss, vtl);
7475
19a0d58f 7476 g_free (str);
8da84040
RN
7477}
7478
7479
dc2c040e 7480static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
7481{
7482 /* if we haven't sync'ed yet, we don't have time to do more. */
745fda83
RN
7483 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7484 GList *iter = g_list_last ( vtl->current_track->trackpoints );
c31b3fbb 7485 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
745fda83
RN
7486
7487 static GdkPixmap *pixmap = NULL;
7488 int w1, h1, w2, h2;
7489 // Need to check in case window has been resized
7490 w1 = vik_viewport_get_width(vvp);
7491 h1 = vik_viewport_get_height(vvp);
7492 if (!pixmap) {
7493 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7494 }
7495 gdk_drawable_get_size (pixmap, &w2, &h2);
7496 if (w1 != w2 || h1 != h2) {
7497 g_object_unref ( G_OBJECT ( pixmap ) );
7498 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7499 }
7500
7501 // Reset to background
7502 gdk_draw_drawable (pixmap,
7503 vtl->current_track_newpoint_gc,
7504 vik_viewport_get_pixmap(vvp),
7505 0, 0, 0, 0, -1, -1);
7506
7507 draw_sync_t *passalong;
7b203521
EB
7508 gint x1, y1;
7509
c31b3fbb 7510 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
745fda83
RN
7511
7512 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7513 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7514 // thus when we come to reset to the background it would include what we have already drawn!!
7515 gdk_draw_line ( pixmap,
7516 vtl->current_track_newpoint_gc,
7517 x1, y1, event->x, event->y );
7518 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
23ea1329 7519
23ea1329
RN
7520 /* Find out actual distance of current track */
7521 gdouble distance = vik_track_get_length (vtl->current_track);
7522
7523 // Now add distance to where the pointer is //
7524 VikCoord coord;
7525 struct LatLon ll;
7526 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7527 vik_coord_to_latlon ( &coord, &ll );
c31b3fbb 7528 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
23ea1329 7529
8da84040
RN
7530 // Get elevation data
7531 gdouble elev_gain, elev_loss;
7532 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7533
7534 // Adjust elevation data (if available) for the current pointer position
7535 gdouble elev_new;
7536 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7537 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
c31b3fbb 7538 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8da84040 7539 // Adjust elevation of last track point
c31b3fbb 7540 if ( elev_new > last_tpt->altitude )
8da84040 7541 // Going up
c31b3fbb 7542 elev_gain += elev_new - last_tpt->altitude;
8da84040
RN
7543 else
7544 // Going down
c31b3fbb 7545 elev_loss += last_tpt->altitude - elev_new;
23ea1329 7546 }
23ea1329 7547 }
8da84040 7548
19a0d58f 7549 gchar *str = distance_string (distance);
6ba8e356 7550
061ccfdc 7551 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
56cb1807 7552 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
061ccfdc
RN
7553
7554 pango_layout_set_text (pl, str, -1);
6ba8e356
RN
7555 gint wd, hd;
7556 pango_layout_get_pixel_size ( pl, &wd, &hd );
7557
56cb1807
RN
7558 gint xd,yd;
7559 // offset from cursor a bit depending on font size
7560 xd = event->x + 10;
7561 yd = event->y - hd;
7562
6ba8e356
RN
7563 // Create a background block to make the text easier to read over the background map
7564 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7565 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
061ccfdc 7566 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
745fda83 7567
6ba8e356
RN
7568 g_object_unref ( G_OBJECT ( pl ) );
7569 g_object_unref ( G_OBJECT ( background_block_gc ) );
7570
745fda83 7571 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7b203521 7572 passalong->vtl = vtl;
745fda83
RN
7573 passalong->pixmap = pixmap;
7574 passalong->drawable = GTK_WIDGET(vvp)->window;
7575 passalong->gc = vtl->current_track_newpoint_gc;
8da84040
RN
7576
7577 // Update statusbar with full gain/loss information
7578 statusbar_write (str, elev_gain, elev_loss, vtl);
7b203521 7579
19a0d58f 7580 g_free (str);
745fda83
RN
7581
7582 // draw pixmap when we have time to
7583 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7584 vtl->draw_sync_done = FALSE;
165d30aa 7585 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 7586 }
165d30aa 7587 return VIK_LAYER_TOOL_ACK;
7b203521
EB
7588}
7589
777e2d4d
EB
7590static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7591{
7592 if ( vtl->current_track && event->keyval == GDK_Escape ) {
7593 vtl->current_track = NULL;
f01eebd4 7594 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
777e2d4d
EB
7595 return TRUE;
7596 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7597 /* undo */
7598 if ( vtl->current_track->trackpoints )
7599 {
7600 GList *last = g_list_last(vtl->current_track->trackpoints);
7601 g_free ( last->data );
7602 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7603 }
b1453c16 7604
8da84040
RN
7605 update_statusbar ( vtl );
7606
f01eebd4 7607 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
777e2d4d
EB
7608 return TRUE;
7609 }
7610 return FALSE;
7611}
7612
e37b2a6d
RN
7613/*
7614 * Common function to handle trackpoint button requests on either a route or a track
7615 * . enables adding a point via normal click
7616 * . enables removal of last point via right click
7617 * . finishing of the track or route via double clicking
7618 */
7619static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
7620{
7621 VikTrackpoint *tp;
7622
941aa6e9
AF
7623 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7624 return FALSE;
7625
ef5e8132
RN
7626 if ( event->button == 2 ) {
7627 // As the display is panning, the new track pixmap is now invalid so don't draw it
7628 // otherwise this drawing done results in flickering back to an old image
7629 vtl->draw_sync_do = FALSE;
7630 return FALSE;
7631 }
7632
e37b2a6d 7633 if ( event->button == 3 )
50a14534 7634 {
e37b2a6d
RN
7635 if ( !vtl->current_track )
7636 return FALSE;
50a14534
EB
7637 /* undo */
7638 if ( vtl->current_track->trackpoints )
7639 {
7640 GList *last = g_list_last(vtl->current_track->trackpoints);
7641 g_free ( last->data );
7642 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7643 }
8da84040
RN
7644 update_statusbar ( vtl );
7645
f01eebd4 7646 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
7647 return TRUE;
7648 }
7649
7650 if ( event->type == GDK_2BUTTON_PRESS )
7651 {
7652 /* subtract last (duplicate from double click) tp then end */
7653 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7654 {
7655 GList *last = g_list_last(vtl->current_track->trackpoints);
7656 g_free ( last->data );
7657 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7658 /* undo last, then end */
7659 vtl->current_track = NULL;
7660 }
f01eebd4 7661 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
7662 return TRUE;
7663 }
7664
50a14534
EB
7665 tp = vik_trackpoint_new();
7666 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7667
7668 /* snap to other TP */
7669 if ( event->state & GDK_CONTROL_MASK )
7670 {
7671 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7672 if ( other_tp )
7673 tp->coord = other_tp->coord;
7674 }
7675
7676 tp->newsegment = FALSE;
7677 tp->has_timestamp = FALSE;
7678 tp->timestamp = 0;
e37b2a6d
RN
7679
7680 if ( vtl->current_track ) {
7681 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7682 /* Auto attempt to get elevation from DEM data (if it's available) */
7683 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7684 }
50a14534
EB
7685
7686 vtl->ct_x1 = vtl->ct_x2;
7687 vtl->ct_y1 = vtl->ct_y2;
7688 vtl->ct_x2 = event->x;
7689 vtl->ct_y2 = event->y;
7690
f01eebd4 7691 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
7692 return TRUE;
7693}
7694
e37b2a6d
RN
7695static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7696{
7697 // ----------------------------------------------------- if current is a route - switch to new track
7698 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7699 {
7700 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7701 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7702 {
7703 vtl->current_track = vik_track_new();
7704 vtl->current_track->visible = TRUE;
7705 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
7706 }
7707 else
7708 return TRUE;
7709 }
7710 return tool_new_track_or_route_click ( vtl, event, vvp );
7711}
7712
ef5e8132
RN
7713static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7714{
7715 if ( event->button == 2 ) {
7716 // Pan moving ended - enable potential point drawing again
7717 vtl->draw_sync_do = TRUE;
7718 vtl->draw_sync_done = TRUE;
7719 }
7720}
7721
e37b2a6d
RN
7722/*** New route ****/
7723
7724static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7725{
7726 return vvp;
7727}
7728
7729static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7730{
7731 // -------------------------- if current is a track - switch to new route
7732 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7733 {
7734 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7735 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
b1453c16 7736 new_route_create_common ( vtl, name );
e37b2a6d
RN
7737 else
7738 return TRUE;
7739 }
7740 return tool_new_track_or_route_click ( vtl, event, vvp );
7741}
7742
941aa6e9
AF
7743/*** New waypoint ****/
7744
7745static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7746{
7747 return vvp;
7748}
7749
7750static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7751{
7752 VikCoord coord;
7753 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7754 return FALSE;
7755 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7756 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
f01eebd4 7757 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
7758 return TRUE;
7759}
7760
7761
7762/*** Edit trackpoint ****/
7763
7764static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7765{
7432fddf 7766 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
7767 t->vvp = vvp;
7768 t->holding = FALSE;
7769 return t;
941aa6e9
AF
7770}
7771
33534cd8 7772static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 7773{
7432fddf 7774 tool_ed_t *t = data;
33534cd8
AF
7775 VikViewport *vvp = t->vvp;
7776 TPSearchParams params;
941aa6e9
AF
7777 /* OUTDATED DOCUMENTATION:
7778 find 5 pixel range on each side. then put these UTM, and a pointer
7779 to the winning track name (and maybe the winning track itself), and a
7780 pointer to the winning trackpoint, inside an array or struct. pass
7781 this along, do a foreach on the tracks which will do a foreach on the
7782 trackpoints. */
7783 params.vvp = vvp;
7784 params.x = event->x;
7785 params.y = event->y;
ce4bd1cf 7786 params.closest_track_id = NULL;
941aa6e9
AF
7787 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7788 params.closest_tp = NULL;
7789
7432fddf
AF
7790 if ( event->button != 1 )
7791 return FALSE;
7792
941aa6e9
AF
7793 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7794 return FALSE;
7795
0d2b891f 7796 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
87741170
RN
7797 return FALSE;
7798
941aa6e9
AF
7799 if ( vtl->current_tpl )
7800 {
7801 /* 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.) */
7802 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
ce4bd1cf
RN
7803 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7804 if ( !current_tr )
7805 return FALSE;
941aa6e9 7806
ce4bd1cf 7807 gint x, y;
941aa6e9
AF
7808 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7809
7810 if ( current_tr->visible &&
7811 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
7812 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7813 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
7814 return TRUE;
7815 }
7816
941aa6e9
AF
7817 }
7818
0d2b891f
RN
7819 if ( vtl->tracks_visible )
7820 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
941aa6e9
AF
7821
7822 if ( params.closest_tp )
7823 {
ce4bd1cf 7824 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
941aa6e9 7825 vtl->current_tpl = params.closest_tpl;
ce4bd1cf 7826 vtl->current_tp_id = params.closest_track_id;
da14cc69 7827 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
941aa6e9 7828 trw_layer_tpwin_init ( vtl );
95d1b757 7829 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
f01eebd4 7830 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
7831 return TRUE;
7832 }
7833
0d2b891f
RN
7834 if ( vtl->routes_visible )
7835 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
7836
7837 if ( params.closest_tp )
7838 {
7839 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7840 vtl->current_tpl = params.closest_tpl;
7841 vtl->current_tp_id = params.closest_track_id;
7842 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7843 trw_layer_tpwin_init ( vtl );
7844 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7845 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7846 return TRUE;
7847 }
7848
941aa6e9
AF
7849 /* these aren't the droids you're looking for */
7850 return FALSE;
7851}
7852
dc2c040e 7853static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 7854{
7432fddf 7855 tool_ed_t *t = data;
33534cd8
AF
7856 VikViewport *vvp = t->vvp;
7857
7858 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7859 return FALSE;
7860
7861 if ( t->holding )
7862 {
7863 VikCoord new_coord;
33534cd8
AF
7864 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7865
7866 /* snap to TP */
7867 if ( event->state & GDK_CONTROL_MASK )
7868 {
7869 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7870 if ( tp && tp != vtl->current_tpl->data )
7871 new_coord = tp->coord;
7872 }
7873 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
7874 {
7875 gint x, y;
7876 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7877 marker_moveto ( t, x, y );
7878 }
33534cd8
AF
7879
7880 return TRUE;
7881 }
7882 return FALSE;
7883}
7884
33534cd8 7885static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 7886{
7432fddf 7887 tool_ed_t *t = data;
33534cd8
AF
7888 VikViewport *vvp = t->vvp;
7889
941aa6e9
AF
7890 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7891 return FALSE;
7432fddf
AF
7892 if ( event->button != 1)
7893 return FALSE;
33534cd8 7894
7432fddf 7895 if ( t->holding ) {
941aa6e9 7896 VikCoord new_coord;
941aa6e9
AF
7897 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7898
7899 /* snap to TP */
7900 if ( event->state & GDK_CONTROL_MASK )
7901 {
7902 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7903 if ( tp && tp != vtl->current_tpl->data )
7904 new_coord = tp->coord;
7905 }
7906
7907 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7908
7432fddf 7909 marker_end_move ( t );
33534cd8 7910
941aa6e9 7911 /* diff dist is diff from orig */
46b6631a 7912 if ( vtl->tpwin )
ce4bd1cf 7913 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
941aa6e9 7914
f01eebd4 7915 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
7916 return TRUE;
7917 }
7918 return FALSE;
7919}
7920
7921
55340efa 7922#ifdef VIK_CONFIG_GOOGLE
7ff7d728
RN
7923/*** Route Finder ***/
7924static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
1eef1bde
QT
7925{
7926 return vvp;
7927}
7928
7ff7d728 7929static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
1eef1bde
QT
7930{
7931 VikCoord tmp;
0c1044e9 7932 if ( !vtl ) return FALSE;
1eef1bde 7933 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7ff7d728 7934 if ( event->button == 3 && vtl->route_finder_current_track ) {
c3deba01 7935 VikCoord *new_end;
7ff7d728 7936 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
c3deba01 7937 if ( new_end ) {
7ff7d728 7938 vtl->route_finder_coord = *new_end;
c3deba01 7939 g_free ( new_end );
f01eebd4 7940 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
c3deba01 7941 /* remove last ' to:...' */
7ff7d728
RN
7942 if ( vtl->route_finder_current_track->comment ) {
7943 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
7944 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
7945 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
7946 last_to - vtl->route_finder_current_track->comment - 1);
7947 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
c3deba01
EB
7948 }
7949 }
7950 }
7951 }
7ff7d728 7952 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
1eef1bde 7953 struct LatLon start, end;
533bbf34
MA
7954 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
7955 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
7956 gchar *url;
bddd2056 7957
7ff7d728 7958 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
1eef1bde 7959 vik_coord_to_latlon ( &(tmp), &end );
7ff7d728 7960 vtl->route_finder_coord = tmp; /* for continuations */
bddd2056
EB
7961
7962 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7ff7d728
RN
7963 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
7964 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
bddd2056 7965 } else {
7ff7d728
RN
7966 vtl->route_finder_check_added_track = TRUE;
7967 vtl->route_finder_started = FALSE;
bddd2056
EB
7968 }
7969
533bbf34
MA
7970 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
7971 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
7972 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
7973 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
7974 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
008e972c 7975 // NB normally this returns a GPX Route - so subsequent usage of it must lookup via the routes hash
55340efa 7976 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
533bbf34 7977 g_free ( url );
bddd2056
EB
7978
7979 /* see if anything was done -- a track was added or appended to */
ce4bd1cf
RN
7980 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
7981 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
7982 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
7983 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
7984 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
7985 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
bddd2056 7986 }
ce4bd1cf 7987 vtl->route_finder_added_track = NULL;
7ff7d728
RN
7988 vtl->route_finder_check_added_track = FALSE;
7989 vtl->route_finder_append = FALSE;
bddd2056 7990
f01eebd4 7991 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
1eef1bde 7992 } else {
7ff7d728
RN
7993 vtl->route_finder_started = TRUE;
7994 vtl->route_finder_coord = tmp;
7995 vtl->route_finder_current_track = NULL;
1eef1bde 7996 }
1eef1bde
QT
7997 return TRUE;
7998}
ebf1bd39 7999#endif
1eef1bde 8000
941aa6e9
AF
8001/*** Show picture ****/
8002
8003static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8004{
8005 return vvp;
8006}
8007
8008/* Params are: vvp, event, last match found or NULL */
8a14f396 8009static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
941aa6e9
AF
8010{
8011 if ( wp->image && wp->visible )
8012 {
8013 gint x, y, slackx, slacky;
8014 GdkEventButton *event = (GdkEventButton *) params[1];
8015
8016 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8017 slackx = wp->image_width / 2;
8018 slacky = wp->image_height / 2;
8019 if ( x <= event->x + slackx && x >= event->x - slackx
8020 && y <= event->y + slacky && y >= event->y - slacky )
8021 {
8022 params[2] = wp->image; /* we've found a match. however continue searching
8023 * since we want to find the last match -- that
8024 * is, the match that was drawn last. */
8025 }
8026 }
8027}
8028
a412f3f5
RN
8029static void trw_layer_show_picture ( gpointer pass_along[6] )
8030{
8031 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8032#ifdef WINDOWS
8b0cdf69 8033 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
a412f3f5
RN
8034#else /* WINDOWS */
8035 GError *err = NULL;
8036 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
3317dc4e 8037 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
a412f3f5
RN
8038 g_free ( quoted_file );
8039 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8040 {
3317dc4e 8041 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
8042 g_error_free ( err );
8043 }
8044 g_free ( cmd );
8045#endif /* WINDOWS */
8046}
8047
941aa6e9
AF
8048static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8049{
8050 gpointer params[3] = { vvp, event, NULL };
8051 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8052 return FALSE;
8053 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8054 if ( params[2] )
8055 {
a412f3f5
RN
8056 static gpointer pass_along[6];
8057 pass_along[0] = vtl;
8058 pass_along[5] = params[2];
8059 trw_layer_show_picture ( pass_along );
941aa6e9
AF
8060 return TRUE; /* found a match */
8061 }
8062 else
8063 return FALSE; /* go through other layers, searching for a match */
8064}
8065
8066/***************************************************************************
8067 ** End tool code
8068 ***************************************************************************/
8069
8070
8071
8072
8073
50a14534
EB
8074static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
8075{
8076 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8077 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8078}
8079
91822ddd
RN
8080/* Structure for thumbnail creating data used in the background thread */
8081typedef struct {
8082 VikTrwLayer *vtl; // Layer needed for redrawing
8083 GSList *pics; // Image list
8084} thumbnail_create_thread_data;
8085
8086static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 8087{
91822ddd
RN
8088 guint total = g_slist_length(tctd->pics), done = 0;
8089 while ( tctd->pics )
50a14534 8090 {
91822ddd 8091 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
8092 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8093 if ( result != 0 )
8094 return -1; /* Abort thread */
8095
91822ddd 8096 tctd->pics = tctd->pics->next;
50a14534 8097 }
91822ddd
RN
8098
8099 // Redraw to show the thumbnails as they are now created
91822ddd 8100 if ( IS_VIK_LAYER(tctd->vtl) )
f01eebd4 8101 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
91822ddd 8102
17c8aefa 8103 return 0;
50a14534
EB
8104}
8105
91822ddd 8106static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 8107{
91822ddd 8108 while ( tctd->pics )
50a14534 8109 {
91822ddd
RN
8110 g_free ( tctd->pics->data );
8111 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 8112 }
91822ddd 8113 g_free ( tctd );
50a14534
EB
8114}
8115
b3eb3b98 8116void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
50a14534
EB
8117{
8118 if ( ! vtl->has_verified_thumbnails )
8119 {
8120 GSList *pics = NULL;
8121 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8122 if ( pics )
8123 {
8124 gint len = g_slist_length ( pics );
4c77d5e0 8125 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
8126 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8127 tctd->vtl = vtl;
8128 tctd->pics = pics;
8129 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8130 tmp,
8131 (vik_thr_func) create_thumbnails_thread,
8132 tctd,
8133 (vik_thr_free_func) thumbnail_create_thread_free,
8134 NULL,
8135 len );
50a14534
EB
8136 g_free ( tmp );
8137 }
8138 }
8139}
8140
b1453c16
RN
8141static const gchar* my_track_colors ( gint ii )
8142{
8143 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8144 "#2d870a",
ca738b73 8145 "#135D34",
b1453c16
RN
8146 "#0a8783",
8147 "#0e4d87",
8148 "#05469f",
ca738b73 8149 "#695CBB",
b1453c16
RN
8150 "#2d059f",
8151 "#4a059f",
ca738b73 8152 "#5A171A",
b1453c16
RN
8153 "#96059f"
8154 };
8155 // Fast and reliable way of returning a colour
8156 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8157}
8158
8159static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8160{
8161 GHashTableIter iter;
8162 gpointer key, value;
8163
8164 gint ii = 0;
8165 // Tracks
8166 g_hash_table_iter_init ( &iter, vtl->tracks );
8167
8168 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8169
8170 // Tracks get a random spread of colours if not already assigned
8171 if ( ! VIK_TRACK(value)->has_color ) {
8172 gdk_color_parse ( my_track_colors (ii) , &(VIK_TRACK(value)->color) );
8173 VIK_TRACK(value)->has_color = TRUE;
8174 }
8175
8176 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8177
8178 ii++;
8179 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8180 ii = 0;
8181 }
8182
8183 // Routes
8184 ii = 0;
8185 g_hash_table_iter_init ( &iter, vtl->routes );
8186
8187 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8188
8189 // Routes get an intermix of reds
8190 if ( ! VIK_TRACK(value)->has_color ) {
8191 if ( ii )
8192 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8193 else
8194 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8195 VIK_TRACK(value)->has_color = TRUE;
8196 }
8197
8198 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8199
8200 ii = !ii;
8201 }
8202}
8203
8204static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vp )
8205{
8206 trw_layer_verify_thumbnails ( vtl, vp );
8207 trw_layer_track_alloc_colors ( vtl );
8208}
8209
50a14534
EB
8210VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8211{
8212 return vtl->coord_mode;
8213}
8214
073ede8c
RN
8215/**
8216 * Uniquify the whole layer
8217 * Also requires the layers panel as the names shown there need updating too
8218 * Returns whether the operation was successful or not
8219 */
8220gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8221{
8222 if ( vtl && vlp ) {
0d2b891f
RN
8223 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8224 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
073ede8c
RN
8225 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8226 return TRUE;
8227 }
8228 return FALSE;
8229}
50a14534 8230
c9570f86 8231static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
50a14534
EB
8232{
8233 vik_coord_convert ( &(wp->coord), *dest_mode );
8234}
8235
8236static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
8237{
8238 vik_track_convert ( tr, *dest_mode );
8239}
8240
8241static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8242{
8243 if ( vtl->coord_mode != dest_mode )
8244 {
8245 vtl->coord_mode = dest_mode;
8246 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8247 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8248 }
8249}
e4afc73a 8250
a7cd93ac 8251static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
20c7a3a0
QT
8252{
8253 vtl->menu_selection = selection;
8254}
8255
a7cd93ac 8256static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
20c7a3a0 8257{
a7cd93ac 8258 return (vtl->menu_selection);
20c7a3a0
QT
8259}
8260
7114e879
QT
8261/* ----------- Downloading maps along tracks --------------- */
8262
8263static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8264{
8265 /* TODO: calculating based on current size of viewport */
8266 const gdouble w_at_zoom_0_125 = 0.0013;
8267 const gdouble h_at_zoom_0_125 = 0.0011;
8268 gdouble zoom_factor = zoom_level/0.125;
8269
8270 wh->lat = h_at_zoom_0_125 * zoom_factor;
8271 wh->lon = w_at_zoom_0_125 * zoom_factor;
8272
8273 return 0; /* all OK */
8274}
8275
35e22ed8
QT
8276static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8277{
8278 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8279 (dist->lat >= ABS(to->north_south - from->north_south)))
8280 return NULL;
8281
8282 VikCoord *coord = g_malloc(sizeof(VikCoord));
8283 coord->mode = VIK_COORD_LATLON;
8284
8285 if (ABS(gradient) < 1) {
8286 if (from->east_west > to->east_west)
8287 coord->east_west = from->east_west - dist->lon;
8288 else
8289 coord->east_west = from->east_west + dist->lon;
8290 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8291 } else {
8292 if (from->north_south > to->north_south)
8293 coord->north_south = from->north_south - dist->lat;
8294 else
8295 coord->north_south = from->north_south + dist->lat;
8296 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8297 }
8298
8299 return coord;
8300}
8301
8302static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8303{
8304 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8305 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8306
8307 VikCoord *next = from;
8308 while (TRUE) {
8309 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8310 break;
8311 list = g_list_prepend(list, next);
8312 }
8313
8314 return list;
8315}
8316
7114e879
QT
8317void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8318{
8319 typedef struct _Rect {
8320 VikCoord tl;
8321 VikCoord br;
35e22ed8 8322 VikCoord center;
7114e879 8323 } Rect;
35e22ed8 8324#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
8325
8326 struct LatLon wh;
35e22ed8 8327 GList *rects_to_download = NULL;
7114e879
QT
8328 GList *rect_iter;
8329
8330 if (get_download_area_width(vvp, zoom_level, &wh))
8331 return;
8332
8333 GList *iter = tr->trackpoints;
35e22ed8
QT
8334 if (!iter)
8335 return;
7114e879
QT
8336
8337 gboolean new_map = TRUE;
8338 VikCoord *cur_coord, tl, br;
8339 Rect *rect;
7114e879
QT
8340 while (iter) {
8341 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8342 if (new_map) {
8343 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8344 rect = g_malloc(sizeof(Rect));
8345 rect->tl = tl;
8346 rect->br = br;
35e22ed8
QT
8347 rect->center = *cur_coord;
8348 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
8349 new_map = FALSE;
8350 iter = iter->next;
8351 continue;
8352 }
8353 gboolean found = FALSE;
35e22ed8
QT
8354 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8355 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
8356 found = TRUE;
8357 break;
8358 }
8359 }
8360 if (found)
8361 iter = iter->next;
8362 else
8363 new_map = TRUE;
8364 }
35e22ed8 8365
35e22ed8 8366 GList *fillins = NULL;
b1e57d16
RN
8367 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8368 /* seems that ATM the function get_next_coord works only for LATLON */
8369 if ( cur_coord->mode == VIK_COORD_LATLON ) {
8370 /* fill-ins for far apart points */
8371 GList *cur_rect, *next_rect;
8372 for (cur_rect = rects_to_download;
8373 (next_rect = cur_rect->next) != NULL;
8374 cur_rect = cur_rect->next) {
8375 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8376 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8377 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8378 }
35e22ed8 8379 }
3cbbb49e
GB
8380 } else
8381 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
8382
8383 if (fillins) {
8384 GList *iter = fillins;
8385 while (iter) {
8386 cur_coord = (VikCoord *)(iter->data);
8387 vik_coord_set_area(cur_coord, &wh, &tl, &br);
8388 rect = g_malloc(sizeof(Rect));
8389 rect->tl = tl;
8390 rect->br = br;
8391 rect->center = *cur_coord;
8392 rects_to_download = g_list_prepend(rects_to_download, rect);
8393 iter = iter->next;
8394 }
8395 }
8396
8397 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
903dc0b4 8398 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7114e879
QT
8399 }
8400
35e22ed8
QT
8401 if (fillins) {
8402 for (iter = fillins; iter; iter = iter->next)
8403 g_free(iter->data);
8404 g_list_free(fillins);
8405 }
8406 if (rects_to_download) {
8407 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8408 g_free(rect_iter->data);
8409 g_list_free(rects_to_download);
8410 }
7114e879
QT
8411}
8412
6bb72350 8413static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7114e879
QT
8414{
8415 VikMapsLayer *vml;
8416 gint selected_map, default_map;
8417 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8418 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8419 gint selected_zoom, default_zoom;
8420 int i,j;
8421
8422
8423 VikTrwLayer *vtl = pass_along[0];
8424 VikLayersPanel *vlp = pass_along[1];
0d2b891f
RN
8425 VikTrack *trk;
8426 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8427 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8428 else
8429 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8430 if ( !trk )
8431 return;
8432
7114e879
QT
8433 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8434
aa7ed888 8435 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
8436 int num_maps = g_list_length(vmls);
8437
8438 if (!num_maps) {
4c77d5e0 8439 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7114e879
QT
8440 return;
8441 }
8442
8443 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8444 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8445
8446 gchar **np = map_names;
8447 VikMapsLayer **lp = map_layers;
8448 for (i = 0; i < num_maps; i++) {
8449 gboolean dup = FALSE;
8450 vml = (VikMapsLayer *)(vmls->data);
8451 for (j = 0; j < i; j++) { /* no duplicate allowed */
8452 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8453 dup = TRUE;
8454 break;
8455 }
8456 }
8457 if (!dup) {
8458 *lp++ = vml;
8459 *np++ = vik_maps_layer_get_map_label(vml);
8460 }
8461 vmls = vmls->next;
8462 }
8463 *lp = NULL;
8464 *np = NULL;
8465 num_maps = lp - map_layers;
8466
8467 for (default_map = 0; default_map < num_maps; default_map++) {
8468 /* TODO: check for parent layer's visibility */
8469 if (VIK_LAYER(map_layers[default_map])->visible)
8470 break;
8471 }
8472 default_map = (default_map == num_maps) ? 0 : default_map;
8473
8474 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8475 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8476 if (cur_zoom == zoom_vals[default_zoom])
8477 break;
8478 }
8479 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8480
8481 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8482 goto done;
8483
0d2b891f 8484 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7114e879
QT
8485
8486done:
8487 for (i = 0; i < num_maps; i++)
8488 g_free(map_names[i]);
8489 g_free(map_names);
8490 g_free(map_layers);
8491
8492 g_list_free(vmls);
8493
8494}
0c1044e9 8495
a8fe53f8
EB
8496/**** lowest waypoint number calculation ***/
8497static gint highest_wp_number_name_to_number(const gchar *name) {
8498 if ( strlen(name) == 3 ) {
8499 int n = atoi(name);
8500 if ( n < 100 && name[0] != '0' )
8501 return -1;
8502 if ( n < 10 && name[0] != '0' )
8503 return -1;
8504 return n;
8505 }
8506 return -1;
8507}
8508
8509
8510static void highest_wp_number_reset(VikTrwLayer *vtl)
8511{
8512 vtl->highest_wp_number = -1;
8513}
8514
8515static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8516{
8517 /* if is bigger that top, add it */
8518 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8519 if ( new_wp_num > vtl->highest_wp_number )
8520 vtl->highest_wp_number = new_wp_num;
8521}
8522
8523static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8524{
8525 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8526 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8527 if ( vtl->highest_wp_number == old_wp_num ) {
8528 gchar buf[4];
c9570f86 8529 vtl->highest_wp_number--;
a8fe53f8
EB
8530
8531 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8532 /* search down until we find something that *does* exist */
8533
c9570f86
RN
8534 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8535 vtl->highest_wp_number--;
a8fe53f8
EB
8536 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8537 }
8538 }
8539}
8540
8541/* get lowest unused number */
8542static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8543{
8544 gchar buf[4];
8545 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 8546 return NULL;
a8fe53f8
EB
8547 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8548 return g_strdup(buf);
8549}