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