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