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