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