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