]> git.street.me.uk Git - andy/viking.git/blame_incremental - src/viktrwlayer.c
[QA] Minor function rename for consistency
[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 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27/* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
28
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
33#include "viking.h"
34#include "vikmapslayer.h"
35#include "vikgpslayer.h"
36#include "viktrwlayer_export.h"
37#include "viktrwlayer_tpwin.h"
38#include "viktrwlayer_wpwin.h"
39#include "viktrwlayer_propwin.h"
40#include "viktrwlayer_analysis.h"
41#include "viktrwlayer_tracklist.h"
42#include "viktrwlayer_waypointlist.h"
43#ifdef VIK_CONFIG_GEOTAG
44#include "viktrwlayer_geotag.h"
45#include "geotag_exif.h"
46#endif
47#include "garminsymbols.h"
48#include "thumbnails.h"
49#include "background.h"
50#include "gpx.h"
51#include "geojson.h"
52#include "babel.h"
53#include "dem.h"
54#include "dems.h"
55#include "geonamessearch.h"
56#ifdef VIK_CONFIG_OPENSTREETMAP
57#include "osm-traces.h"
58#endif
59#include "acquire.h"
60#include "datasources.h"
61#include "datasource_gps.h"
62#include "vikexttool_datasources.h"
63#include "ui_util.h"
64#include "vikutils.h"
65
66#include "vikrouting.h"
67
68#include "icons/icons.h"
69
70#ifdef HAVE_MATH_H
71#include <math.h>
72#endif
73#ifdef HAVE_STRING_H
74#include <string.h>
75#endif
76#ifdef HAVE_STDLIB_H
77#include <stdlib.h>
78#endif
79#include <stdio.h>
80#include <ctype.h>
81
82#include <gdk/gdkkeysyms.h>
83#include <glib.h>
84#include <glib/gstdio.h>
85#include <glib/gi18n.h>
86
87#define VIK_TRW_LAYER_TRACK_GC 6
88#define VIK_TRW_LAYER_TRACK_GCS 10
89#define VIK_TRW_LAYER_TRACK_GC_BLACK 0
90#define VIK_TRW_LAYER_TRACK_GC_SLOW 1
91#define VIK_TRW_LAYER_TRACK_GC_AVER 2
92#define VIK_TRW_LAYER_TRACK_GC_FAST 3
93#define VIK_TRW_LAYER_TRACK_GC_STOP 4
94#define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
95
96#define DRAWMODE_BY_TRACK 0
97#define DRAWMODE_BY_SPEED 1
98#define DRAWMODE_ALL_SAME_COLOR 2
99// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
100// as we are (re)calculating the colour for every point
101
102#define POINTS 1
103#define LINES 2
104
105/* this is how it knows when you click if you are clicking close to a trackpoint. */
106#define TRACKPOINT_SIZE_APPROX 5
107#define WAYPOINT_SIZE_APPROX 5
108
109#define MIN_STOP_LENGTH 15
110#define MAX_STOP_LENGTH 86400
111#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
112 /* this is multiplied by user-inputted value from 1-100. */
113
114enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
115
116// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
117typedef enum {
118 FS_XX_SMALL = 0, // 'xx-small'
119 FS_X_SMALL,
120 FS_SMALL,
121 FS_MEDIUM, // DEFAULT
122 FS_LARGE,
123 FS_X_LARGE,
124 FS_XX_LARGE,
125 FS_NUM_SIZES
126} font_size_t;
127
128struct _VikTrwLayer {
129 VikLayer vl;
130 GHashTable *tracks;
131 GHashTable *tracks_iters;
132 GHashTable *routes;
133 GHashTable *routes_iters;
134 GHashTable *waypoints_iters;
135 GHashTable *waypoints;
136 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
137 gboolean tracks_visible, routes_visible, waypoints_visible;
138 LatLonBBox waypoints_bbox;
139
140 gboolean track_draw_labels;
141 guint8 drawmode;
142 guint8 drawpoints;
143 guint8 drawpoints_size;
144 guint8 drawelevation;
145 guint8 elevation_factor;
146 guint8 drawstops;
147 guint32 stop_length;
148 guint8 drawlines;
149 guint8 drawdirections;
150 guint8 drawdirections_size;
151 guint8 line_thickness;
152 guint8 bg_line_thickness;
153 vik_layer_sort_order_t track_sort_order;
154
155 // Metadata
156 VikTRWMetadata *metadata;
157
158 PangoLayout *tracklabellayout;
159 font_size_t track_font_size;
160 gchar *track_fsize_str;
161
162 guint8 wp_symbol;
163 guint8 wp_size;
164 gboolean wp_draw_symbols;
165 font_size_t wp_font_size;
166 gchar *wp_fsize_str;
167 vik_layer_sort_order_t wp_sort_order;
168
169 gdouble track_draw_speed_factor;
170 GArray *track_gc;
171 GdkGC *track_1color_gc;
172 GdkColor track_color;
173 GdkGC *current_track_gc;
174 // Separate GC for a track's potential new point as drawn via separate method
175 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
176 GdkGC *current_track_newpoint_gc;
177 GdkGC *track_bg_gc; GdkColor track_bg_color;
178 GdkGC *waypoint_gc; GdkColor waypoint_color;
179 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
180 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
181 gboolean wpbgand;
182 GdkFont *waypoint_font;
183 VikTrack *current_track; // ATM shared between new tracks and new routes
184 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
185 gboolean draw_sync_done;
186 gboolean draw_sync_do;
187
188 VikCoordMode coord_mode;
189
190 /* wp editing tool */
191 VikWaypoint *current_wp;
192 gpointer current_wp_id;
193 gboolean moving_wp;
194 gboolean waypoint_rightclick;
195
196 /* track editing tool */
197 GList *current_tpl;
198 VikTrack *current_tp_track;
199 gpointer current_tp_id;
200 VikTrwLayerTpwin *tpwin;
201
202 /* track editing tool -- more specifically, moving tps */
203 gboolean moving_tp;
204
205 /* route finder tool */
206 gboolean route_finder_started;
207 gboolean route_finder_check_added_track;
208 VikTrack *route_finder_added_track;
209 gboolean route_finder_append;
210
211 gboolean drawlabels;
212 gboolean drawimages;
213 guint8 image_alpha;
214 GQueue *image_cache;
215 guint8 image_size;
216 guint16 image_cache_size;
217
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
220
221 gboolean has_verified_thumbnails;
222
223 GtkMenu *wp_right_click_menu;
224 GtkMenu *track_right_click_menu;
225
226 /* menu */
227 VikStdLayerMenuItem menu_selection;
228
229 gint highest_wp_number;
230
231 // One per layer
232 GtkWidget *tracks_analysis_dialog;
233};
234
235/* A caached waypoint image. */
236typedef struct {
237 GdkPixbuf *pixbuf;
238 gchar *image; /* filename */
239} CachedPixbuf;
240
241struct DrawingParams {
242 VikViewport *vp;
243 VikTrwLayer *vtl;
244 VikWindow *vw;
245 gdouble xmpp, ympp;
246 guint16 width, height;
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
249 const VikCoord *center;
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
252 LatLonBBox bbox;
253 gboolean highlight;
254};
255
256static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
257
258typedef enum {
259 MA_VTL = 0,
260 MA_VLP,
261 MA_SUBTYPE, // OR END for Layer only
262 MA_SUBLAYER_ID,
263 MA_CONFIRM,
264 MA_VVP,
265 MA_TV_ITER,
266 MA_MISC,
267 MA_LAST,
268} menu_array_index;
269
270typedef gpointer menu_array_layer[2];
271typedef gpointer menu_array_sublayer[MA_LAST];
272
273static void trw_layer_delete_item ( menu_array_sublayer values );
274static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275static void trw_layer_cut_item_cb ( menu_array_sublayer values );
276
277static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
278static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
279
280static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
282
283static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
285
286static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
287static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292static void trw_layer_goto_track_center ( menu_array_sublayer values );
293static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295static void trw_layer_merge_with_other ( menu_array_sublayer values );
296static void trw_layer_append_track ( menu_array_sublayer values );
297static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300static void trw_layer_split_segments ( menu_array_sublayer values );
301static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304static void trw_layer_reverse ( menu_array_sublayer values );
305static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307static void trw_layer_show_picture ( menu_array_sublayer values );
308static void trw_layer_gps_upload_any ( menu_array_sublayer values );
309
310static void trw_layer_centerize ( menu_array_layer values );
311static void trw_layer_auto_view ( menu_array_layer values );
312static void trw_layer_goto_wp ( menu_array_layer values );
313static void trw_layer_new_wp ( menu_array_layer values );
314static void trw_layer_new_track ( menu_array_layer values );
315static void trw_layer_new_route ( menu_array_layer values );
316static void trw_layer_finish_track ( menu_array_layer values );
317static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318static void trw_layer_auto_tracks_view ( menu_array_layer values );
319static void trw_layer_delete_all_tracks ( menu_array_layer values );
320static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
325#ifdef VIK_CONFIG_GEOTAG
326static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328static void trw_layer_geotagging_track ( menu_array_sublayer values );
329static void trw_layer_geotagging ( menu_array_layer values );
330#endif
331static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333static void trw_layer_acquire_url_cb ( menu_array_layer values );
334#ifdef VIK_CONFIG_OPENSTREETMAP
335static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
337#endif
338#ifdef VIK_CONFIG_GEOCACHES
339static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
340#endif
341#ifdef VIK_CONFIG_GEOTAG
342static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
343#endif
344static void trw_layer_acquire_file_cb ( menu_array_layer values );
345static void trw_layer_gps_upload ( menu_array_layer values );
346
347static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
348static void trw_layer_track_list_dialog ( menu_array_layer values );
349static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
350
351// Specific route versions:
352// Most track handling functions can handle operating on the route list
353// However these ones are easier in separate functions
354static void trw_layer_auto_routes_view ( menu_array_layer values );
355static void trw_layer_delete_all_routes ( menu_array_layer values );
356static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
357
358/* pop-up items */
359static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
363
364static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
365static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
366
367static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
371
372static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
379static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
389static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
391static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
396
397static void cached_pixbuf_free ( CachedPixbuf *cp );
398static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
399
400static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
402
403static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
404static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
405
406static gchar *highest_wp_number_get(VikTrwLayer *vtl);
407static void highest_wp_number_reset(VikTrwLayer *vtl);
408static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
409static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
410
411// Note for the following tool GtkRadioActionEntry texts:
412// the very first text value is an internal name not displayed anywhere
413// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
414// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
415// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
416// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
417static VikToolInterface trw_layer_tools[] = {
418 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
419 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
420 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
421 FALSE,
422 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
423
424 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
425 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
426 (VikToolMouseFunc) tool_new_track_click,
427 (VikToolMouseMoveFunc) tool_new_track_move,
428 (VikToolMouseFunc) tool_new_track_release,
429 (VikToolKeyFunc) tool_new_track_key_press,
430 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
431 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
432
433 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
434 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
435 (VikToolMouseFunc) tool_new_route_click,
436 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
437 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
438 (VikToolKeyFunc) tool_new_track_key_press, // -/#
439 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
440 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
441
442 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
443 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
444 (VikToolMouseFunc) tool_extended_route_finder_click,
445 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
446 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
447 (VikToolKeyFunc) tool_extended_route_finder_key_press,
448 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
449 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
450
451 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
452 (VikToolConstructorFunc) tool_edit_waypoint_create,
453 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
454 NULL, NULL,
455 (VikToolMouseFunc) tool_edit_waypoint_click,
456 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
457 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
458 FALSE,
459 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
460
461 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
462 (VikToolConstructorFunc) tool_edit_trackpoint_create,
463 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
464 NULL, NULL,
465 (VikToolMouseFunc) tool_edit_trackpoint_click,
466 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
467 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
468 FALSE,
469 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
470
471 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
472 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
473 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
474 FALSE,
475 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
476
477};
478
479enum {
480 TOOL_CREATE_WAYPOINT=0,
481 TOOL_CREATE_TRACK,
482 TOOL_CREATE_ROUTE,
483 TOOL_ROUTE_FINDER,
484 TOOL_EDIT_WAYPOINT,
485 TOOL_EDIT_TRACKPOINT,
486 TOOL_SHOW_PICTURE,
487 NUM_TOOLS
488};
489
490/****** PARAMETERS ******/
491
492static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
493enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
494
495static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
496static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
497
498#define MIN_POINT_SIZE 2
499#define MAX_POINT_SIZE 10
500
501#define MIN_ARROW_SIZE 3
502#define MAX_ARROW_SIZE 20
503
504static VikLayerParamScale params_scales[] = {
505 /* min max step digits */
506 { 1, 10, 1, 0 }, /* line_thickness */
507 { 0, 100, 1, 0 }, /* track draw speed factor */
508 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
509 /* 5 * step == how much to turn */
510 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
511 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
512 { 5, 500, 5, 0 }, // 5: image cache_size - " "
513 { 0, 8, 1, 0 }, // 6: Background line thickness
514 { 1, 64, 1, 0 }, /* wpsize */
515 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
516 { 1, 100, 1, 0 }, // 9: elevation factor
517 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
518 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
519};
520
521static gchar* params_font_sizes[] = {
522 N_("Extra Extra Small"),
523 N_("Extra Small"),
524 N_("Small"),
525 N_("Medium"),
526 N_("Large"),
527 N_("Extra Large"),
528 N_("Extra Extra Large"),
529 NULL };
530
531// Needs to align with vik_layer_sort_order_t
532static gchar* params_sort_order[] = {
533 N_("None"),
534 N_("Name Ascending"),
535 N_("Name Descending"),
536 NULL
537};
538
539static VikLayerParamData black_color_default ( void ) {
540 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
541}
542static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
543static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
544static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
545static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
546static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
547static VikLayerParamData trackbgcolor_default ( void ) {
548 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
549}
550static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
551static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
552static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
553
554static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
555static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
556static VikLayerParamData wptextcolor_default ( void ) {
557 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
558}
559static VikLayerParamData wpbgcolor_default ( void ) {
560 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
561}
562static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
563static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
564
565static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
566static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
567static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
568
569static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
570
571static VikLayerParamData string_default ( void )
572{
573 VikLayerParamData data;
574 data.s = "";
575 return data;
576}
577
578VikLayerParam trw_layer_params[] = {
579 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
580 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
581 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
582
583 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
584 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
585 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
586 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
587 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
588 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
589 { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
590 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
591 { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
592 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
593 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
594 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
595 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
596 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
597 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
598 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
599 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
600
601 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
602 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
603 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
604 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
605 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
606
607 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
608 { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
609 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
610 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
611 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
612 { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
613 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
614 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
615 { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
616 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
617
618 { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
619 { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL, image_size_default, NULL, NULL },
620 { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
621 { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
622
623 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
625 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
626 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
627};
628
629// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
630enum {
631 // Sublayer visibilities
632 PARAM_TV,
633 PARAM_WV,
634 PARAM_RV,
635 // Tracks
636 PARAM_TDL,
637 PARAM_TLFONTSIZE,
638 PARAM_DM,
639 PARAM_TC,
640 PARAM_DL,
641 PARAM_LT,
642 PARAM_DD,
643 PARAM_DDS,
644 PARAM_DP,
645 PARAM_DPS,
646 PARAM_DE,
647 PARAM_EF,
648 PARAM_DS,
649 PARAM_SL,
650 PARAM_BLT,
651 PARAM_TBGC,
652 PARAM_TDSF,
653 PARAM_TSO,
654 // Waypoints
655 PARAM_DLA,
656 PARAM_WPFONTSIZE,
657 PARAM_WPC,
658 PARAM_WPTC,
659 PARAM_WPBC,
660 PARAM_WPBA,
661 PARAM_WPSYM,
662 PARAM_WPSIZE,
663 PARAM_WPSYMS,
664 PARAM_WPSO,
665 // WP images
666 PARAM_DI,
667 PARAM_IS,
668 PARAM_IA,
669 PARAM_ICS,
670 // Metadata
671 PARAM_MDDESC,
672 PARAM_MDAUTH,
673 PARAM_MDTIME,
674 PARAM_MDKEYS,
675 NUM_PARAMS
676};
677
678/*** TO ADD A PARAM:
679 *** 1) Add to trw_layer_params and enumeration
680 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
681 ***/
682
683/****** END PARAMETERS ******/
684
685/* Layer Interface function definitions */
686static VikTrwLayer* trw_layer_create ( VikViewport *vp );
687static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
688static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
689static void trw_layer_free ( VikTrwLayer *trwlayer );
690static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
691static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
692static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
693static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
694static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
695static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
696static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
697static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
698static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
699static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
700static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
701static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
702static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
703static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
704static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
705static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
706static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
707static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
708static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
709static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
710static void trw_layer_free_copied_item ( gint subtype, gpointer item );
711static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
712static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
713static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
714static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
715static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
716/* End Layer Interface function definitions */
717
718VikLayerInterface vik_trw_layer_interface = {
719 "TrackWaypoint",
720 N_("TrackWaypoint"),
721 "<control><shift>Y",
722 &viktrwlayer_pixbuf,
723
724 trw_layer_tools,
725 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
726
727 trw_layer_params,
728 NUM_PARAMS,
729 params_groups, /* params_groups */
730 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
731
732 VIK_MENU_ITEM_ALL,
733
734 (VikLayerFuncCreate) trw_layer_create,
735 (VikLayerFuncRealize) trw_layer_realize,
736 (VikLayerFuncPostRead) trw_layer_post_read,
737 (VikLayerFuncFree) trw_layer_free,
738
739 (VikLayerFuncProperties) NULL,
740 (VikLayerFuncDraw) trw_layer_draw,
741 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
742
743 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
744 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
745
746 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
747 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
748
749 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
750 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
751 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
752 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
753 (VikLayerFuncLayerSelected) trw_layer_selected,
754
755 (VikLayerFuncMarshall) trw_layer_marshall,
756 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
757
758 (VikLayerFuncSetParam) trw_layer_set_param,
759 (VikLayerFuncGetParam) trw_layer_get_param,
760 (VikLayerFuncChangeParam) trw_layer_change_param,
761
762 (VikLayerFuncReadFileData) a_gpspoint_read_file,
763 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
764
765 (VikLayerFuncDeleteItem) trw_layer_del_item,
766 (VikLayerFuncCutItem) trw_layer_cut_item,
767 (VikLayerFuncCopyItem) trw_layer_copy_item,
768 (VikLayerFuncPasteItem) trw_layer_paste_item,
769 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
770
771 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
772
773 (VikLayerFuncSelectClick) trw_layer_select_click,
774 (VikLayerFuncSelectMove) trw_layer_select_move,
775 (VikLayerFuncSelectRelease) trw_layer_select_release,
776 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
777};
778
779static gboolean have_diary_program = FALSE;
780static gboolean have_geojson_export = FALSE;
781
782
783// NB Only performed once per program run
784static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
785{
786 if ( g_find_program_in_path( "rednotebook" ) ) {
787 gchar *mystdout = NULL;
788 gchar *mystderr = NULL;
789 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
790 if ( g_spawn_command_line_sync ( "rednotebook --version", &mystdout, &mystderr, NULL, NULL ) ) {
791 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
792 if ( stdout )
793 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
794 if ( stderr )
795 g_warning ("Diary: stderr: %s", mystderr );
796
797 gchar **tokens = NULL;
798 if ( stdout && g_strcmp0(mystdout, "") )
799 tokens = g_strsplit(mystdout, " ", 0);
800 else if ( stderr )
801 tokens = g_strsplit(mystderr, " ", 0);
802
803 gint num = 0;
804 gchar *token = tokens[num];
805 while ( token && num < 2 ) {
806 if (num == 1) {
807 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
808 have_diary_program = TRUE;
809 }
810 num++;
811 token = tokens[num];
812 }
813 g_strfreev ( tokens );
814 }
815 g_free ( mystdout );
816 g_free ( mystderr );
817 }
818
819 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
820 have_geojson_export = TRUE;
821 }
822}
823
824GType vik_trw_layer_get_type ()
825{
826 static GType vtl_type = 0;
827
828 if (!vtl_type)
829 {
830 static const GTypeInfo vtl_info =
831 {
832 sizeof (VikTrwLayerClass),
833 NULL, /* base_init */
834 NULL, /* base_finalize */
835 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
836 NULL, /* class_finalize */
837 NULL, /* class_data */
838 sizeof (VikTrwLayer),
839 0,
840 NULL /* instance init */
841 };
842 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
843 }
844 return vtl_type;
845}
846
847VikTRWMetadata *vik_trw_metadata_new()
848{
849 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
850}
851
852void vik_trw_metadata_free ( VikTRWMetadata *metadata)
853{
854 g_free (metadata);
855}
856
857VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
858{
859 return vtl->metadata;
860}
861
862void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
863{
864 if ( vtl->metadata )
865 vik_trw_metadata_free ( vtl->metadata );
866 vtl->metadata = metadata;
867}
868
869typedef struct {
870 gboolean found;
871 const gchar *date_str;
872 const VikTrack *trk;
873 const VikWaypoint *wpt;
874 gpointer *trk_id;
875 gpointer *wpt_id;
876} date_finder_type;
877
878static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
879{
880 gchar date_buf[20];
881 date_buf[0] = '\0';
882 // Might be an easier way to compare dates rather than converting the strings all the time...
883 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
884 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
885
886 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
887 df->found = TRUE;
888 df->trk = trk;
889 df->trk_id = id;
890 }
891 }
892 return df->found;
893}
894
895static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
896{
897 gchar date_buf[20];
898 date_buf[0] = '\0';
899 // Might be an easier way to compare dates rather than converting the strings all the time...
900 if ( wpt->has_timestamp ) {
901 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
902
903 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
904 df->found = TRUE;
905 df->wpt = wpt;
906 df->wpt_id = id;
907 }
908 }
909 return df->found;
910}
911
912/**
913 * Find an item by date
914 */
915gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
916{
917 date_finder_type df;
918 df.found = FALSE;
919 df.date_str = date_str;
920 df.trk = NULL;
921 df.wpt = NULL;
922 // Only tracks ATM
923 if ( do_tracks )
924 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
925 else
926 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
927
928 if ( select && df.found ) {
929 if ( do_tracks && df.trk ) {
930 struct LatLon maxmin[2] = { {0,0}, {0,0} };
931 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
932 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
933 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
934 }
935 else if ( df.wpt ) {
936 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
937 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
938 }
939 vik_layer_emit_update ( VIK_LAYER(vtl) );
940 }
941 return df.found;
942}
943
944static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
945{
946 static menu_array_sublayer values;
947 if (!sublayer) {
948 return;
949 }
950
951 gint ii;
952 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
953 values[ii] = NULL;
954
955 values[MA_VTL] = vtl;
956 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
957 values[MA_SUBLAYER_ID] = sublayer;
958 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
959
960 trw_layer_delete_item ( values );
961}
962
963static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
964{
965 static menu_array_sublayer values;
966 if (!sublayer) {
967 return;
968 }
969
970 gint ii;
971 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
972 values[ii] = NULL;
973
974 values[MA_VTL] = vtl;
975 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
976 values[MA_SUBLAYER_ID] = sublayer;
977 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
978
979 trw_layer_copy_item_cb(values);
980 trw_layer_cut_item_cb(values);
981}
982
983static void trw_layer_copy_item_cb ( menu_array_sublayer values)
984{
985 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
986 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
987 gpointer * sublayer = values[MA_SUBLAYER_ID];
988 guint8 *data = NULL;
989 guint len;
990
991 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
992
993 if (data) {
994 const gchar* name;
995 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
996 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
997 if ( wp && wp->name )
998 name = wp->name;
999 else
1000 name = NULL; // Broken :(
1001 }
1002 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1003 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1004 if ( trk && trk->name )
1005 name = trk->name;
1006 else
1007 name = NULL; // Broken :(
1008 }
1009 else {
1010 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1011 if ( trk && trk->name )
1012 name = trk->name;
1013 else
1014 name = NULL; // Broken :(
1015 }
1016
1017 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1018 subtype, len, name, data);
1019 }
1020}
1021
1022static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1023{
1024 trw_layer_copy_item_cb(values);
1025 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1026 trw_layer_delete_item(values);
1027}
1028
1029static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1030{
1031 // Slightly cheating method, routing via the panels capability
1032 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1033}
1034
1035static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1036{
1037 guint8 *id;
1038 guint il;
1039
1040 if (!sublayer) {
1041 *item = NULL;
1042 return;
1043 }
1044
1045 GByteArray *ba = g_byte_array_new ();
1046
1047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1048 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1049 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1050 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1051 } else {
1052 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1053 }
1054
1055 g_byte_array_append ( ba, id, il );
1056
1057 g_free(id);
1058
1059 *len = ba->len;
1060 *item = ba->data;
1061}
1062
1063static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1064{
1065 if ( !item )
1066 return FALSE;
1067
1068 gchar *name;
1069
1070 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1071 {
1072 VikWaypoint *w;
1073
1074 w = vik_waypoint_unmarshall ( item, len );
1075 // When copying - we'll create a new name based on the original
1076 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1077 vik_trw_layer_add_waypoint ( vtl, name, w );
1078 waypoint_convert (NULL, w, &vtl->coord_mode);
1079 g_free ( name );
1080
1081 trw_layer_calculate_bounds_waypoints ( vtl );
1082
1083 // Consider if redraw necessary for the new item
1084 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1085 vik_layer_emit_update ( VIK_LAYER(vtl) );
1086 return TRUE;
1087 }
1088 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1089 {
1090 VikTrack *t;
1091
1092 t = vik_track_unmarshall ( item, len );
1093 // When copying - we'll create a new name based on the original
1094 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1095 vik_trw_layer_add_track ( vtl, name, t );
1096 vik_track_convert (t, vtl->coord_mode);
1097 g_free ( name );
1098
1099 // Consider if redraw necessary for the new item
1100 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1101 vik_layer_emit_update ( VIK_LAYER(vtl) );
1102 return TRUE;
1103 }
1104 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1105 {
1106 VikTrack *t;
1107
1108 t = vik_track_unmarshall ( item, len );
1109 // When copying - we'll create a new name based on the original
1110 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1111 vik_trw_layer_add_route ( vtl, name, t );
1112 vik_track_convert (t, vtl->coord_mode);
1113 g_free ( name );
1114
1115 // Consider if redraw necessary for the new item
1116 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1117 vik_layer_emit_update ( VIK_LAYER(vtl) );
1118 return TRUE;
1119 }
1120 return FALSE;
1121}
1122
1123static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1124{
1125 if (item) {
1126 g_free(item);
1127 }
1128}
1129
1130static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1131{
1132 switch ( id )
1133 {
1134 case PARAM_TV: vtl->tracks_visible = data.b; break;
1135 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1136 case PARAM_RV: vtl->routes_visible = data.b; break;
1137 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1138 case PARAM_TLFONTSIZE:
1139 if ( data.u < FS_NUM_SIZES ) {
1140 vtl->track_font_size = data.u;
1141 g_free ( vtl->track_fsize_str );
1142 switch ( vtl->track_font_size ) {
1143 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1144 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1145 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1146 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1147 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1148 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1149 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1150 }
1151 }
1152 break;
1153 case PARAM_DM: vtl->drawmode = data.u; break;
1154 case PARAM_TC:
1155 vtl->track_color = data.c;
1156 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1157 break;
1158 case PARAM_DP: vtl->drawpoints = data.b; break;
1159 case PARAM_DPS:
1160 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1161 vtl->drawpoints_size = data.u;
1162 break;
1163 case PARAM_DE: vtl->drawelevation = data.b; break;
1164 case PARAM_DS: vtl->drawstops = data.b; break;
1165 case PARAM_DL: vtl->drawlines = data.b; break;
1166 case PARAM_DD: vtl->drawdirections = data.b; break;
1167 case PARAM_DDS:
1168 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1169 vtl->drawdirections_size = data.u;
1170 break;
1171 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1172 vtl->stop_length = data.u;
1173 break;
1174 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1175 vtl->elevation_factor = data.u;
1176 break;
1177 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1178 {
1179 vtl->line_thickness = data.u;
1180 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1181 }
1182 break;
1183 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1184 {
1185 vtl->bg_line_thickness = data.u;
1186 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1187 }
1188 break;
1189 case PARAM_TBGC:
1190 vtl->track_bg_color = data.c;
1191 if ( vtl->track_bg_gc )
1192 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1193 break;
1194 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1195 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1196 case PARAM_DLA: vtl->drawlabels = data.b; break;
1197 case PARAM_DI: vtl->drawimages = data.b; break;
1198 case PARAM_IS: if ( data.u != vtl->image_size )
1199 {
1200 vtl->image_size = data.u;
1201 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1202 g_queue_free ( vtl->image_cache );
1203 vtl->image_cache = g_queue_new ();
1204 }
1205 break;
1206 case PARAM_IA: vtl->image_alpha = data.u; break;
1207 case PARAM_ICS: vtl->image_cache_size = data.u;
1208 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1209 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1210 break;
1211 case PARAM_WPC:
1212 vtl->waypoint_color = data.c;
1213 if ( vtl->waypoint_gc )
1214 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1215 break;
1216 case PARAM_WPTC:
1217 vtl->waypoint_text_color = data.c;
1218 if ( vtl->waypoint_text_gc )
1219 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1220 break;
1221 case PARAM_WPBC:
1222 vtl->waypoint_bg_color = data.c;
1223 if ( vtl->waypoint_bg_gc )
1224 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1225 break;
1226 case PARAM_WPBA:
1227 vtl->wpbgand = data.b;
1228 if ( vtl->waypoint_bg_gc )
1229 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1230 break;
1231 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1232 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1233 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1234 case PARAM_WPFONTSIZE:
1235 if ( data.u < FS_NUM_SIZES ) {
1236 vtl->wp_font_size = data.u;
1237 g_free ( vtl->wp_fsize_str );
1238 switch ( vtl->wp_font_size ) {
1239 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1240 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1241 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1242 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1243 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1244 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1245 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1246 }
1247 }
1248 break;
1249 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1250 // Metadata
1251 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1252 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1253 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1254 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1255 default: break;
1256 }
1257 return TRUE;
1258}
1259
1260static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1261{
1262 VikLayerParamData rv;
1263 switch ( id )
1264 {
1265 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1266 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1267 case PARAM_RV: rv.b = vtl->routes_visible; break;
1268 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1269 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1270 case PARAM_DM: rv.u = vtl->drawmode; break;
1271 case PARAM_TC: rv.c = vtl->track_color; break;
1272 case PARAM_DP: rv.b = vtl->drawpoints; break;
1273 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1274 case PARAM_DE: rv.b = vtl->drawelevation; break;
1275 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1276 case PARAM_DS: rv.b = vtl->drawstops; break;
1277 case PARAM_SL: rv.u = vtl->stop_length; break;
1278 case PARAM_DL: rv.b = vtl->drawlines; break;
1279 case PARAM_DD: rv.b = vtl->drawdirections; break;
1280 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1281 case PARAM_LT: rv.u = vtl->line_thickness; break;
1282 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1283 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1284 case PARAM_DI: rv.b = vtl->drawimages; break;
1285 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1286 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1287 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1288 case PARAM_IS: rv.u = vtl->image_size; break;
1289 case PARAM_IA: rv.u = vtl->image_alpha; break;
1290 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1291 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1292 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1293 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1294 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1295 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1296 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1297 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1298 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1299 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1300 // Metadata
1301 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1302 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1303 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1304 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1305 default: break;
1306 }
1307 return rv;
1308}
1309
1310static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1311{
1312 // This '-3' is to account for the first few parameters not in the properties
1313 const gint OFFSET = -3;
1314
1315 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1316 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1317 case PARAM_DI: {
1318 // Get new value
1319 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1320 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1321 GtkWidget **ww2 = values[UI_CHG_LABELS];
1322 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1323 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1324 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1325 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1326 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1327 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1328 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1329 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1330 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1331 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1332 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1333 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1334 break;
1335 }
1336 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1337 case PARAM_DLA: {
1338 // Get new value
1339 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1340 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1341 GtkWidget **ww2 = values[UI_CHG_LABELS];
1342 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1343 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1344 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1345 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1346 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1347 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1348 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1349 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1350 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1351 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1352 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1353 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1354 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1355 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1356 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1357 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1358 break;
1359 }
1360 // Alter sensitivity of all track colours according to the draw track mode.
1361 case PARAM_DM: {
1362 // Get new value
1363 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1364 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1365 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1366 GtkWidget **ww2 = values[UI_CHG_LABELS];
1367 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1368 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1369 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1370 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1371 break;
1372 }
1373 case PARAM_MDTIME: {
1374 // Force metadata->timestamp to be always read-only for now.
1375 GtkWidget **ww = values[UI_CHG_WIDGETS];
1376 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1377 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1378 }
1379 // NB Since other track settings have been split across tabs,
1380 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1381 default: break;
1382 }
1383}
1384
1385static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1386{
1387 guint8 *pd;
1388 gint pl;
1389
1390 *data = NULL;
1391
1392 // Use byte arrays to store sublayer data
1393 // much like done elsewhere e.g. vik_layer_marshall_params()
1394 GByteArray *ba = g_byte_array_new ( );
1395
1396 guint8 *sl_data;
1397 guint sl_len;
1398
1399 guint object_length;
1400 guint subtype;
1401 // store:
1402 // the length of the item
1403 // the sublayer type of item
1404 // the the actual item
1405#define tlm_append(object_pointer, size, type) \
1406 subtype = (type); \
1407 object_length = (size); \
1408 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1409 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1410 g_byte_array_append ( ba, (object_pointer), object_length );
1411
1412 // Layer parameters first
1413 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1414 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1415 g_byte_array_append ( ba, pd, pl );
1416 g_free ( pd );
1417
1418 // Now sublayer data
1419 GHashTableIter iter;
1420 gpointer key, value;
1421
1422 // Waypoints
1423 g_hash_table_iter_init ( &iter, vtl->waypoints );
1424 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1425 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1426 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1427 g_free ( sl_data );
1428 }
1429
1430 // Tracks
1431 g_hash_table_iter_init ( &iter, vtl->tracks );
1432 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1433 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1434 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1435 g_free ( sl_data );
1436 }
1437
1438 // Routes
1439 g_hash_table_iter_init ( &iter, vtl->routes );
1440 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1441 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1442 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1443 g_free ( sl_data );
1444 }
1445
1446#undef tlm_append
1447
1448 *data = ba->data;
1449 *len = ba->len;
1450}
1451
1452static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1453{
1454 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1455 gint pl;
1456 gint consumed_length;
1457
1458 // First the overall layer parameters
1459 memcpy(&pl, data, sizeof(pl));
1460 data += sizeof(pl);
1461 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1462 data += pl;
1463
1464 consumed_length = pl;
1465 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1466
1467#define tlm_size (*(gint *)data)
1468 // See marshalling above for order of how this is written
1469#define tlm_next \
1470 data += sizeof_len_and_subtype + tlm_size;
1471
1472 // Now the individual sublayers:
1473
1474 while ( *data && consumed_length < len ) {
1475 // Normally four extra bytes at the end of the datastream
1476 // (since it's a GByteArray and that's where it's length is stored)
1477 // So only attempt read when there's an actual block of sublayer data
1478 if ( consumed_length + tlm_size < len ) {
1479
1480 // Reuse pl to read the subtype from the data stream
1481 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1482
1483 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1484 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1485 gchar *name = g_strdup ( trk->name );
1486 vik_trw_layer_add_track ( vtl, name, trk );
1487 g_free ( name );
1488 }
1489 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1490 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1491 gchar *name = g_strdup ( wp->name );
1492 vik_trw_layer_add_waypoint ( vtl, name, wp );
1493 g_free ( name );
1494 }
1495 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1496 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1497 gchar *name = g_strdup ( trk->name );
1498 vik_trw_layer_add_route ( vtl, name, trk );
1499 g_free ( name );
1500 }
1501 }
1502 consumed_length += tlm_size + sizeof_len_and_subtype;
1503 tlm_next;
1504 }
1505 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1506
1507 // Not stored anywhere else so need to regenerate
1508 trw_layer_calculate_bounds_waypoints ( vtl );
1509
1510 return vtl;
1511}
1512
1513// Keep interesting hash function at least visible
1514/*
1515static guint strcase_hash(gconstpointer v)
1516{
1517 // 31 bit hash function
1518 int i;
1519 const gchar *t = v;
1520 gchar s[128]; // malloc is too slow for reading big files
1521 gchar *p = s;
1522
1523 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1524 p[i] = toupper(t[i]);
1525 p[i] = '\0';
1526
1527 p = s;
1528 guint32 h = *p;
1529 if (h) {
1530 for (p += 1; *p != '\0'; p++)
1531 h = (h << 5) - h + *p;
1532 }
1533
1534 return h;
1535}
1536*/
1537
1538// Stick a 1 at the end of the function name to make it more unique
1539// thus more easily searchable in a simple text editor
1540static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1541{
1542 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1543 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1544
1545 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1546 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1547
1548 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1549 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1550 // and with normal PC processing capabilities - it has negligibile performance impact
1551 // This also minimized the amount of rework - as the management of the hash tables already exists.
1552
1553 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1554 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1555 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1556
1557 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1558 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1559 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1560 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1561 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1562 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1563
1564 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1565
1566 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1567
1568 // Param settings that are not available via the GUI
1569 // Force to on after processing params (which defaults them to off with a zero value)
1570 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1571
1572 rv->metadata = vik_trw_metadata_new ();
1573 rv->draw_sync_done = TRUE;
1574 rv->draw_sync_do = TRUE;
1575 // Everything else is 0, FALSE or NULL
1576
1577 return rv;
1578}
1579
1580
1581static void trw_layer_free ( VikTrwLayer *trwlayer )
1582{
1583 g_hash_table_destroy(trwlayer->waypoints);
1584 g_hash_table_destroy(trwlayer->waypoints_iters);
1585 g_hash_table_destroy(trwlayer->tracks);
1586 g_hash_table_destroy(trwlayer->tracks_iters);
1587 g_hash_table_destroy(trwlayer->routes);
1588 g_hash_table_destroy(trwlayer->routes_iters);
1589
1590 /* ODC: replace with GArray */
1591 trw_layer_free_track_gcs ( trwlayer );
1592
1593 if ( trwlayer->wp_right_click_menu )
1594 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1595
1596 if ( trwlayer->track_right_click_menu )
1597 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1598
1599 if ( trwlayer->tracklabellayout != NULL)
1600 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1601
1602 if ( trwlayer->wplabellayout != NULL)
1603 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1604
1605 if ( trwlayer->waypoint_gc != NULL )
1606 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1607
1608 if ( trwlayer->waypoint_text_gc != NULL )
1609 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1610
1611 if ( trwlayer->waypoint_bg_gc != NULL )
1612 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1613
1614 g_free ( trwlayer->wp_fsize_str );
1615 g_free ( trwlayer->track_fsize_str );
1616
1617 if ( trwlayer->tpwin != NULL )
1618 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1619
1620 if ( trwlayer->tracks_analysis_dialog != NULL )
1621 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1622
1623 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1624 g_queue_free ( trwlayer->image_cache );
1625}
1626
1627static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1628{
1629 dp->vtl = vtl;
1630 dp->vp = vp;
1631 dp->highlight = highlight;
1632 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1633 dp->xmpp = vik_viewport_get_xmpp ( vp );
1634 dp->ympp = vik_viewport_get_ympp ( vp );
1635 dp->width = vik_viewport_get_width ( vp );
1636 dp->height = vik_viewport_get_height ( vp );
1637 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1638 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1639
1640 dp->center = vik_viewport_get_center ( vp );
1641 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1642 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1643
1644 if ( dp->one_zone )
1645 {
1646 gint w2, h2;
1647 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1648 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1649 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1650
1651 dp->ce1 = dp->center->east_west-w2;
1652 dp->ce2 = dp->center->east_west+w2;
1653 dp->cn1 = dp->center->north_south-h2;
1654 dp->cn2 = dp->center->north_south+h2;
1655 } else if ( dp->lat_lon ) {
1656 VikCoord upperleft, bottomright;
1657 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1658 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1659 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1660 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1661 dp->ce1 = upperleft.east_west;
1662 dp->ce2 = bottomright.east_west;
1663 dp->cn1 = bottomright.north_south;
1664 dp->cn2 = upperleft.north_south;
1665 }
1666
1667 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1668}
1669
1670/*
1671 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1672 * Here a simple traffic like light colour system is used:
1673 * . slow points are red
1674 * . average is yellow
1675 * . fast points are green
1676 */
1677static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1678{
1679 gdouble rv = 0;
1680 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1681 if ( average_speed > 0 ) {
1682 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1683 if ( rv < low_speed )
1684 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1685 else if ( rv > high_speed )
1686 return VIK_TRW_LAYER_TRACK_GC_FAST;
1687 else
1688 return VIK_TRW_LAYER_TRACK_GC_AVER;
1689 }
1690 }
1691 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1692}
1693
1694static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1695{
1696 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1697 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1698 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1699 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1700}
1701
1702
1703static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1704{
1705 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1706
1707 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1708 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1709 else
1710 // Fallback if parse failure
1711 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1712
1713 g_free ( label_markup );
1714
1715 gint label_x, label_y;
1716 gint width, height;
1717 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1718
1719 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1720 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1721}
1722
1723/**
1724 * distance_in_preferred_units:
1725 * @dist: The source distance in standard SI Units (i.e. metres)
1726 *
1727 * TODO: This is a generic function that could be moved into globals.c or utils.c
1728 *
1729 * Probably best used if you have a only few conversions to perform.
1730 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1731 * since it will be doing the preference check on each call
1732 *
1733 * Returns: The distance in the units as specified by the preferences
1734 */
1735static gdouble distance_in_preferred_units ( gdouble dist )
1736{
1737 gdouble mydist;
1738 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1739 switch (dist_units) {
1740 case VIK_UNITS_DISTANCE_MILES:
1741 mydist = VIK_METERS_TO_MILES(dist);
1742 break;
1743 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1744 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1745 break;
1746 // VIK_UNITS_DISTANCE_KILOMETRES:
1747 default:
1748 mydist = dist/1000.0;
1749 break;
1750 }
1751 return mydist;
1752}
1753
1754/**
1755 * trw_layer_draw_dist_labels:
1756 *
1757 * Draw a few labels along a track at nicely seperated distances
1758 * This might slow things down if there's many tracks being displayed with this on.
1759 */
1760static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1761{
1762 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1763 25.0, 40.0, 50.0, 75.0, 100.0,
1764 150.0, 200.0, 250.0, 500.0, 1000.0};
1765
1766 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1767
1768 // Convert to specified unit to find the friendly breakdown value
1769 dist = distance_in_preferred_units ( dist );
1770
1771 gint index = 0;
1772 gint i=0;
1773 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1774 if ( chunksd[i] > dist ) {
1775 index = i;
1776 dist = chunksd[index];
1777 break;
1778 }
1779 }
1780
1781 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1782
1783 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1784 gdouble dist_i = dist * i;
1785
1786 // Convert distance back into metres for use in finding a trackpoint
1787 switch (dist_units) {
1788 case VIK_UNITS_DISTANCE_MILES:
1789 dist_i = VIK_MILES_TO_METERS(dist_i);
1790 break;
1791 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1792 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1793 break;
1794 // VIK_UNITS_DISTANCE_KILOMETRES:
1795 default:
1796 dist_i = dist_i*1000.0;
1797 break;
1798 }
1799
1800 gdouble dist_current = 0.0;
1801 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1802 gdouble dist_next = 0.0;
1803 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1804
1805 gdouble dist_between_tps = fabs (dist_next - dist_current);
1806 gdouble ratio = 0.0;
1807 // Prevent division by 0 errors
1808 if ( dist_between_tps > 0.0 )
1809 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1810
1811 if ( tp_current && tp_next ) {
1812 // Construct the name based on the distance value
1813 gchar *name;
1814 gchar *units;
1815 switch (dist_units) {
1816 case VIK_UNITS_DISTANCE_MILES:
1817 units = g_strdup ( _("miles") );
1818 break;
1819 // VIK_UNITS_DISTANCE_KILOMETRES:
1820 default:
1821 units = g_strdup ( _("km") );
1822 break;
1823 }
1824
1825 // Convert for display
1826 dist_i = distance_in_preferred_units ( dist_i );
1827
1828 // Make the precision of the output related to the unit size.
1829 if ( index == 0 )
1830 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1831 else if ( index == 1 )
1832 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1833 else
1834 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1835 g_free ( units );
1836
1837 struct LatLon ll_current, ll_next;
1838 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1839 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1840
1841 // positional interpolation
1842 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1843 // but should be good enough over the small scale that I anticipate usage on
1844 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1845 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1846 VikCoord coord;
1847 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1848
1849 gchar *fgcolour;
1850 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1851 fgcolour = gdk_color_to_string ( &(trk->color) );
1852 else
1853 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1854
1855 // if highlight mode on, then colour the background in the highlight colour
1856 gchar *bgcolour;
1857 if ( drawing_highlight )
1858 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1859 else
1860 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1861
1862 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1863
1864 g_free ( fgcolour );
1865 g_free ( bgcolour );
1866 g_free ( name );
1867 }
1868 }
1869}
1870
1871/**
1872 * trw_layer_draw_track_name_labels:
1873 *
1874 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1875 */
1876static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1877{
1878 gchar *fgcolour;
1879 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1880 fgcolour = gdk_color_to_string ( &(trk->color) );
1881 else
1882 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1883
1884 // if highlight mode on, then colour the background in the highlight colour
1885 gchar *bgcolour;
1886 if ( drawing_highlight )
1887 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1888 else
1889 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1890
1891 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1892
1893 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1894 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1895 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1896 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1897 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1898 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1899 VikCoord coord;
1900 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1901
1902 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1903 }
1904
1905 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1906 // No other labels to draw
1907 return;
1908
1909 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1910 if ( !tp_end )
1911 return;
1912 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1913 if ( !tp_begin )
1914 return;
1915 VikCoord begin_coord = tp_begin->coord;
1916 VikCoord end_coord = tp_end->coord;
1917
1918 gboolean done_start_end = FALSE;
1919
1920 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1921 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1922
1923 // This number can be configured via the settings if you really want to change it
1924 gdouble distance_diff;
1925 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1926 distance_diff = 100.0; // Metres
1927
1928 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1929 // Start and end 'close' together so only draw one label at an average location
1930 gint x1, x2, y1, y2;
1931 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1932 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1933 VikCoord av_coord;
1934 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1935
1936 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1937 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1938 g_free ( name );
1939
1940 done_start_end = TRUE;
1941 }
1942 }
1943
1944 if ( ! done_start_end ) {
1945 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1946 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1947 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1948 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1949 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1950 g_free ( name_start );
1951 }
1952 // Don't draw end label if this is the one being created
1953 if ( trk != dp->vtl->current_track ) {
1954 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1955 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1956 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1957 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1958 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1959 g_free ( name_end );
1960 }
1961 }
1962 }
1963
1964 g_free ( fgcolour );
1965 g_free ( bgcolour );
1966 g_free ( ename );
1967}
1968
1969static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1970{
1971 if ( ! track->visible )
1972 return;
1973
1974 /* TODO: this function is a mess, get rid of any redundancy */
1975 GList *list = track->trackpoints;
1976 GdkGC *main_gc;
1977 gboolean useoldvals = TRUE;
1978
1979 gboolean drawpoints;
1980 gboolean drawstops;
1981 gboolean drawelevation;
1982 gdouble min_alt, max_alt, alt_diff = 0;
1983
1984 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1985 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1986 guint8 tp_size;
1987
1988 if ( dp->vtl->drawelevation )
1989 {
1990 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1991 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1992 alt_diff = max_alt - min_alt;
1993 }
1994
1995 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1996 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1997 trw_layer_draw_track ( id, track, dp, TRUE );
1998
1999 if ( draw_track_outline )
2000 drawpoints = drawstops = FALSE;
2001 else {
2002 drawpoints = dp->vtl->drawpoints;
2003 drawstops = dp->vtl->drawstops;
2004 }
2005
2006 gboolean drawing_highlight = FALSE;
2007 /* Current track - used for creation */
2008 if ( track == dp->vtl->current_track )
2009 main_gc = dp->vtl->current_track_gc;
2010 else {
2011 if ( dp->highlight ) {
2012 /* Draw all tracks of the layer in special colour
2013 NB this supercedes the drawmode */
2014 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2015 drawing_highlight = TRUE;
2016 }
2017 if ( !drawing_highlight ) {
2018 // Still need to figure out the gc according to the drawing mode:
2019 switch ( dp->vtl->drawmode ) {
2020 case DRAWMODE_BY_TRACK:
2021 if ( dp->vtl->track_1color_gc )
2022 g_object_unref ( dp->vtl->track_1color_gc );
2023 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2024 main_gc = dp->vtl->track_1color_gc;
2025 break;
2026 default:
2027 // Mostly for DRAWMODE_ALL_SAME_COLOR
2028 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2029 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2030 break;
2031 }
2032 }
2033 }
2034
2035 if (list) {
2036 int x, y, oldx, oldy;
2037 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2038
2039 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2040
2041 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2042
2043 // Draw the first point as something a bit different from the normal points
2044 // ATM it's slightly bigger and a triangle
2045 if ( drawpoints ) {
2046 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2047 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2048 }
2049
2050 oldx = x;
2051 oldy = y;
2052
2053 gdouble average_speed = 0.0;
2054 gdouble low_speed = 0.0;
2055 gdouble high_speed = 0.0;
2056 // If necessary calculate these values - which is done only once per track redraw
2057 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2058 // the percentage factor away from the average speed determines transistions between the levels
2059 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2060 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2061 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2062 }
2063
2064 while ((list = g_list_next(list)))
2065 {
2066 tp = VIK_TRACKPOINT(list->data);
2067 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2068
2069 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2070 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2071 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2072 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2073 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2074 {
2075 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2076
2077 /*
2078 * If points are the same in display coordinates, don't draw.
2079 */
2080 if ( useoldvals && x == oldx && y == oldy )
2081 {
2082 // Still need to process points to ensure 'stops' are drawn if required
2083 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2084 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2085 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 );
2086
2087 goto skip;
2088 }
2089
2090 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2091 if ( drawpoints || dp->vtl->drawlines ) {
2092 // setup main_gc for both point and line drawing
2093 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2094 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
2095 }
2096 }
2097
2098 if ( drawpoints && ! draw_track_outline )
2099 {
2100
2101 if ( list->next ) {
2102 /*
2103 * The concept of drawing stops is that a trackpoint
2104 * that is if the next trackpoint has a timestamp far into
2105 * the future, we draw a circle of 6x trackpoint size,
2106 * instead of a rectangle of 2x trackpoint size.
2107 * This is drawn first so the trackpoint will be drawn on top
2108 */
2109 /* stops */
2110 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2111 /* Stop point. Draw 6x circle. Always in redish colour */
2112 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2113
2114 /* Regular point - draw 2x square. */
2115 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2116 }
2117 else
2118 /* Final point - draw 4x circle. */
2119 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 );
2120 }
2121
2122 if ((!tp->newsegment) && (dp->vtl->drawlines))
2123 {
2124
2125 /* UTM only: zone check */
2126 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2127 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2128
2129 if (!useoldvals)
2130 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2131
2132 if ( draw_track_outline ) {
2133 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2134 }
2135 else {
2136
2137 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2138
2139 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2140 GdkPoint tmp[4];
2141 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2142
2143 tmp[0].x = oldx;
2144 tmp[0].y = oldy;
2145 tmp[1].x = oldx;
2146 tmp[1].y = oldy-FIXALTITUDE(list->data);
2147 tmp[2].x = x;
2148 tmp[2].y = y-FIXALTITUDE(list->next->data);
2149 tmp[3].x = x;
2150 tmp[3].y = y;
2151
2152 GdkGC *tmp_gc;
2153 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2154 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2155 else
2156 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2157 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2158
2159 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2160 }
2161 }
2162 }
2163
2164 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2165 // Draw an arrow at the mid point to show the direction of the track
2166 // Code is a rework from vikwindow::draw_ruler()
2167 gint midx = (oldx + x) / 2;
2168 gint midy = (oldy + y) / 2;
2169
2170 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2171 // Avoid divide by zero and ensure at least 1 pixel big
2172 if ( len > 1 ) {
2173 gdouble dx = (oldx - midx) / len;
2174 gdouble dy = (oldy - midy) / len;
2175 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2176 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2177 }
2178 }
2179
2180 skip:
2181 oldx = x;
2182 oldy = y;
2183 useoldvals = TRUE;
2184 }
2185 else {
2186 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2187 {
2188 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2189 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2190 {
2191 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2192
2193 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2194 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
2195 }
2196
2197 /*
2198 * If points are the same in display coordinates, don't draw.
2199 */
2200 if ( x != oldx || y != oldy )
2201 {
2202 if ( draw_track_outline )
2203 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2204 else
2205 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2206 }
2207 }
2208 else
2209 {
2210 /*
2211 * If points are the same in display coordinates, don't draw.
2212 */
2213 if ( x != oldx && y != oldy )
2214 {
2215 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2216 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2217 }
2218 }
2219 }
2220 useoldvals = FALSE;
2221 }
2222 }
2223
2224 // Labels drawn after the trackpoints, so the labels are on top
2225 if ( dp->vtl->track_draw_labels ) {
2226 if ( track->max_number_dist_labels > 0 ) {
2227 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2228 }
2229
2230 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2231 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2232 }
2233 }
2234 }
2235}
2236
2237static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2238{
2239 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2240 trw_layer_draw_track ( id, track, dp, FALSE );
2241 }
2242}
2243
2244static void cached_pixbuf_free ( CachedPixbuf *cp )
2245{
2246 g_object_unref ( G_OBJECT(cp->pixbuf) );
2247 g_free ( cp->image );
2248}
2249
2250static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2251{
2252 return strcmp ( cp->image, name );
2253}
2254
2255static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2256{
2257 if ( wp->visible )
2258 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2259 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2260 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2261 {
2262 gint x, y;
2263 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2264
2265 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2266
2267 if ( wp->image && dp->vtl->drawimages )
2268 {
2269 GdkPixbuf *pixbuf = NULL;
2270 GList *l;
2271
2272 if ( dp->vtl->image_alpha == 0)
2273 return;
2274
2275 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2276 if ( l )
2277 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2278 else
2279 {
2280 gchar *image = wp->image;
2281 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2282 if ( ! regularthumb )
2283 {
2284 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2285 image = "\x12\x00"; /* this shouldn't occur naturally. */
2286 }
2287 if ( regularthumb )
2288 {
2289 CachedPixbuf *cp = NULL;
2290 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2291 if ( dp->vtl->image_size == 128 )
2292 cp->pixbuf = regularthumb;
2293 else
2294 {
2295 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2296 g_assert ( cp->pixbuf );
2297 g_object_unref ( G_OBJECT(regularthumb) );
2298 }
2299 cp->image = g_strdup ( image );
2300
2301 /* needed so 'click picture' tool knows how big the pic is; we don't
2302 * store it in cp because they may have been freed already. */
2303 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2304 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2305
2306 g_queue_push_head ( dp->vtl->image_cache, cp );
2307 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2308 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2309
2310 pixbuf = cp->pixbuf;
2311 }
2312 else
2313 {
2314 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2315 }
2316 }
2317 if ( pixbuf )
2318 {
2319 gint w, h;
2320 w = gdk_pixbuf_get_width ( pixbuf );
2321 h = gdk_pixbuf_get_height ( pixbuf );
2322
2323 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2324 {
2325 if ( dp->highlight ) {
2326 // Highlighted - so draw a little border around the chosen one
2327 // single line seems a little weak so draw 2 of them
2328 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2329 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2330 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2331 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2332 }
2333
2334 if ( dp->vtl->image_alpha == 255 )
2335 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2336 else
2337 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2338 }
2339 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2340 }
2341 }
2342
2343 // Draw appropriate symbol - either symbol image or simple types
2344 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2345 vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
2346 }
2347 else if ( wp == dp->vtl->current_wp ) {
2348 switch ( dp->vtl->wp_symbol ) {
2349 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;
2350 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;
2351 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;
2352 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 );
2353 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 );
2354 default: break;
2355 }
2356 }
2357 else {
2358 switch ( dp->vtl->wp_symbol ) {
2359 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;
2360 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;
2361 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;
2362 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 );
2363 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;
2364 default: break;
2365 }
2366 }
2367
2368 if ( dp->vtl->drawlabels )
2369 {
2370 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2371 gint label_x, label_y;
2372 gint width, height;
2373 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2374
2375 // Could this stored in the waypoint rather than recreating each pass?
2376 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2377
2378 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2379 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2380 else
2381 // Fallback if parse failure
2382 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2383
2384 g_free ( wp_label_markup );
2385
2386 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2387 label_x = x - width/2;
2388 if ( wp->symbol_pixbuf )
2389 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2390 else
2391 label_y = y - dp->vtl->wp_size - height - 2;
2392
2393 /* if highlight mode on, then draw background text in highlight colour */
2394 if ( dp->highlight )
2395 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2396 else
2397 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2398 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2399 }
2400 }
2401}
2402
2403static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2404{
2405 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2406 trw_layer_draw_waypoint ( id, wp, dp );
2407 }
2408}
2409
2410static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2411{
2412 static struct DrawingParams dp;
2413 g_assert ( l != NULL );
2414
2415 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2416
2417 if ( l->tracks_visible )
2418 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2419
2420 if ( l->routes_visible )
2421 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2422
2423 if (l->waypoints_visible)
2424 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2425}
2426
2427static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2428{
2429 // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2430 // This may seem slightly inefficient to test each time for every layer
2431 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2432 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2433 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2434 return;
2435 trw_layer_draw_with_highlight ( l, data, FALSE );
2436}
2437
2438void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2439{
2440 // Check the layer for visibility (including all the parents visibilities)
2441 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2442 return;
2443 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2444}
2445
2446/**
2447 * vik_trw_layer_draw_highlight_item:
2448 *
2449 * Only handles a single track or waypoint ATM
2450 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2451 */
2452void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2453{
2454 // Check the layer for visibility (including all the parents visibilities)
2455 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2456 return;
2457
2458 static struct DrawingParams dp;
2459 init_drawing_params ( &dp, vtl, vvp, TRUE );
2460
2461 if ( trk ) {
2462 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2463 if ( draw )
2464 trw_layer_draw_track_cb ( NULL, trk, &dp );
2465 }
2466 if ( vtl->waypoints_visible && wpt ) {
2467 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2468 }
2469}
2470
2471/**
2472 * vik_trw_layer_draw_highlight_item:
2473 *
2474 * Generally for drawing all tracks or routes or waypoints
2475 * trks may be actually routes
2476 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2477 */
2478void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2479{
2480 // Check the layer for visibility (including all the parents visibilities)
2481 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2482 return;
2483
2484 static struct DrawingParams dp;
2485 init_drawing_params ( &dp, vtl, vvp, TRUE );
2486
2487 if ( trks ) {
2488 gboolean is_routes = (trks == vtl->routes);
2489 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2490 if ( draw )
2491 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2492 }
2493
2494 if ( vtl->waypoints_visible && wpts )
2495 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2496}
2497
2498static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2499{
2500 int i;
2501 if ( vtl->track_bg_gc )
2502 {
2503 g_object_unref ( vtl->track_bg_gc );
2504 vtl->track_bg_gc = NULL;
2505 }
2506 if ( vtl->track_1color_gc )
2507 {
2508 g_object_unref ( vtl->track_1color_gc );
2509 vtl->track_1color_gc = NULL;
2510 }
2511 if ( vtl->current_track_gc )
2512 {
2513 g_object_unref ( vtl->current_track_gc );
2514 vtl->current_track_gc = NULL;
2515 }
2516 if ( vtl->current_track_newpoint_gc )
2517 {
2518 g_object_unref ( vtl->current_track_newpoint_gc );
2519 vtl->current_track_newpoint_gc = NULL;
2520 }
2521
2522 if ( ! vtl->track_gc )
2523 return;
2524 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2525 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2526 g_array_free ( vtl->track_gc, TRUE );
2527 vtl->track_gc = NULL;
2528}
2529
2530static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2531{
2532 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2533 gint width = vtl->line_thickness;
2534
2535 if ( vtl->track_gc )
2536 trw_layer_free_track_gcs ( vtl );
2537
2538 if ( vtl->track_bg_gc )
2539 g_object_unref ( vtl->track_bg_gc );
2540 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2541
2542 // Ensure new track drawing heeds line thickness setting
2543 // however always have a minium of 2, as 1 pixel is really narrow
2544 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2545
2546 if ( vtl->current_track_gc )
2547 g_object_unref ( vtl->current_track_gc );
2548 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2549 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2550
2551 // 'newpoint' gc is exactly the same as the current track gc
2552 if ( vtl->current_track_newpoint_gc )
2553 g_object_unref ( vtl->current_track_newpoint_gc );
2554 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2555 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2556
2557 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2558
2559 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2560 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2561
2562 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2563 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2564 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2565
2566 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2567
2568 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2569}
2570
2571static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2572{
2573 VikTrwLayer *rv = trw_layer_new1 ( vp );
2574 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2575
2576 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2577 /* early exit, as the rest is GUI related */
2578 return rv;
2579 }
2580
2581 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2582 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2583
2584 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2585 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2586
2587 trw_layer_new_track_gcs ( rv, vp );
2588
2589 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2590 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2591 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2592 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2593
2594 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2595
2596 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2597
2598 return rv;
2599}
2600
2601#define SMALL_ICON_SIZE 18
2602/*
2603 * Can accept a null symbol, and may return null value
2604 */
2605GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2606{
2607 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2608 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2609 // So needing a small icon for the treeview may need some resizing:
2610 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2611 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2612 return wp_icon;
2613}
2614
2615static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2616{
2617 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2618
2619 GdkPixbuf *pixbuf = NULL;
2620
2621 if ( track->has_color ) {
2622 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2623 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2624 // Here is some magic found to do the conversion
2625 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2626 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2627 ((track->color.green & 0xff00) << 8) |
2628 (track->color.blue & 0xff00);
2629
2630 gdk_pixbuf_fill ( pixbuf, pixel );
2631 }
2632
2633 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
2634
2635 if ( pixbuf )
2636 g_object_unref (pixbuf);
2637
2638 *new_iter = *((GtkTreeIter *) pass_along[1]);
2639 if ( track->is_route )
2640 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2641 else
2642 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2643
2644 if ( ! track->visible )
2645 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2646}
2647
2648static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2649{
2650 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2651
2652 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
2653
2654 *new_iter = *((GtkTreeIter *) pass_along[1]);
2655 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2656
2657 if ( ! wp->visible )
2658 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2659}
2660
2661static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2662{
2663 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2664}
2665
2666static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2667{
2668 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2669}
2670
2671static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2672{
2673 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2674}
2675
2676static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2677{
2678 GtkTreeIter iter2;
2679 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2680
2681 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2682 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2683
2684 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2685
2686 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2687 }
2688
2689 if ( g_hash_table_size (vtl->routes) > 0 ) {
2690 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2691
2692 pass_along[0] = &(vtl->routes_iter);
2693 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2694
2695 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2696
2697 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2698 }
2699
2700 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2701 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2702
2703 pass_along[0] = &(vtl->waypoints_iter);
2704 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2705
2706 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2707
2708 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2709 }
2710
2711}
2712
2713static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2714{
2715 switch ( subtype )
2716 {
2717 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2718 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2719 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2720 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2721 {
2722 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2723 if (t)
2724 return (t->visible ^= 1);
2725 else
2726 return TRUE;
2727 }
2728 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2729 {
2730 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2731 if (t)
2732 return (t->visible ^= 1);
2733 else
2734 return TRUE;
2735 }
2736 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2737 {
2738 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2739 if (t)
2740 return (t->visible ^= 1);
2741 else
2742 return TRUE;
2743 }
2744 default: break;
2745 }
2746 return TRUE;
2747}
2748
2749/*
2750 * Return a property about tracks for this layer
2751 */
2752gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2753{
2754 return vtl->line_thickness;
2755}
2756
2757// Structure to hold multiple track information for a layer
2758typedef struct {
2759 gdouble length;
2760 time_t start_time;
2761 time_t end_time;
2762 gint duration;
2763} tooltip_tracks;
2764
2765/*
2766 * Build up layer multiple track information via updating the tooltip_tracks structure
2767 */
2768static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2769{
2770 tt->length = tt->length + vik_track_get_length (tr);
2771
2772 // Ensure times are available
2773 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2774 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2775 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2776 if ( trkpt_last->has_timestamp ) {
2777 time_t t1, t2;
2778 t1 = vik_track_get_tp_first(tr)->timestamp;
2779 t2 = trkpt_last->timestamp;
2780
2781 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2782 // Hence initialize to the first 'proper' value
2783 if ( tt->start_time == 0 )
2784 tt->start_time = t1;
2785 if ( tt->end_time == 0 )
2786 tt->end_time = t2;
2787
2788 // Update find the earliest / last times
2789 if ( t1 < tt->start_time )
2790 tt->start_time = t1;
2791 if ( t2 > tt->end_time )
2792 tt->end_time = t2;
2793
2794 // Keep track of total time
2795 // there maybe gaps within a track (eg segments)
2796 // but this should be generally good enough for a simple indicator
2797 tt->duration = tt->duration + (int)(t2-t1);
2798 }
2799 }
2800}
2801
2802/*
2803 * Generate tooltip text for the layer.
2804 * This is relatively complicated as it considers information for
2805 * no tracks, a single track or multiple tracks
2806 * (which may or may not have timing information)
2807 */
2808static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2809{
2810 gchar tbuf1[32];
2811 gchar tbuf2[64];
2812 gchar tbuf3[64];
2813 gchar tbuf4[10];
2814 tbuf1[0] = '\0';
2815 tbuf2[0] = '\0';
2816 tbuf3[0] = '\0';
2817 tbuf4[0] = '\0';
2818
2819 static gchar tmp_buf[128];
2820 tmp_buf[0] = '\0';
2821
2822 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2823
2824 // Safety check - I think these should always be valid
2825 if ( vtl->tracks && vtl->waypoints ) {
2826 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2827 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2828
2829 GDate* gdate_start = g_date_new ();
2830 g_date_set_time_t (gdate_start, tt.start_time);
2831
2832 GDate* gdate_end = g_date_new ();
2833 g_date_set_time_t (gdate_end, tt.end_time);
2834
2835 if ( g_date_compare (gdate_start, gdate_end) ) {
2836 // Dates differ so print range on separate line
2837 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2838 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2839 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2840 }
2841 else {
2842 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2843 if ( tt.start_time != 0 )
2844 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2845 }
2846
2847 tbuf2[0] = '\0';
2848 if ( tt.length > 0.0 ) {
2849 gdouble len_in_units;
2850
2851 // Setup info dependent on distance units
2852 switch ( a_vik_get_units_distance() ) {
2853 case VIK_UNITS_DISTANCE_MILES:
2854 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2855 len_in_units = VIK_METERS_TO_MILES(tt.length);
2856 break;
2857 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2858 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2859 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2860 break;
2861 default:
2862 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2863 len_in_units = tt.length/1000.0;
2864 break;
2865 }
2866
2867 // Timing information if available
2868 tbuf1[0] = '\0';
2869 if ( tt.duration > 0 ) {
2870 g_snprintf (tbuf1, sizeof(tbuf1),
2871 _(" in %d:%02d hrs:mins"),
2872 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2873 }
2874 g_snprintf (tbuf2, sizeof(tbuf2),
2875 _("\n%sTotal Length %.1f %s%s"),
2876 tbuf3, len_in_units, tbuf4, tbuf1);
2877 }
2878
2879 // Put together all the elements to form compact tooltip text
2880 g_snprintf (tmp_buf, sizeof(tmp_buf),
2881 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2882 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2883
2884 g_date_free (gdate_start);
2885 g_date_free (gdate_end);
2886
2887 }
2888
2889 return tmp_buf;
2890}
2891
2892static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2893{
2894 switch ( subtype )
2895 {
2896 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2897 {
2898 // Very simple tooltip - may expand detail in the future...
2899 static gchar tmp_buf[32];
2900 g_snprintf (tmp_buf, sizeof(tmp_buf),
2901 _("Tracks: %d"),
2902 g_hash_table_size (l->tracks));
2903 return tmp_buf;
2904 }
2905 break;
2906 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2907 {
2908 // Very simple tooltip - may expand detail in the future...
2909 static gchar tmp_buf[32];
2910 g_snprintf (tmp_buf, sizeof(tmp_buf),
2911 _("Routes: %d"),
2912 g_hash_table_size (l->routes));
2913 return tmp_buf;
2914 }
2915 break;
2916
2917 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2918 // Same tooltip for a route
2919 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2920 {
2921 VikTrack *tr;
2922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2923 tr = g_hash_table_lookup ( l->tracks, sublayer );
2924 else
2925 tr = g_hash_table_lookup ( l->routes, sublayer );
2926
2927 if ( tr ) {
2928 // Could be a better way of handling strings - but this works...
2929 gchar time_buf1[20];
2930 gchar time_buf2[20];
2931 time_buf1[0] = '\0';
2932 time_buf2[0] = '\0';
2933 static gchar tmp_buf[100];
2934 // Compact info: Short date eg (11/20/99), duration and length
2935 // Hopefully these are the things that are most useful and so promoted into the tooltip
2936 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2937 // %x The preferred date representation for the current locale without the time.
2938 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2939 time_t dur = vik_track_get_duration ( tr );
2940 if ( dur > 0 )
2941 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2942 }
2943 // Get length and consider the appropriate distance units
2944 gdouble tr_len = vik_track_get_length(tr);
2945 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2946 switch (dist_units) {
2947 case VIK_UNITS_DISTANCE_KILOMETRES:
2948 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2949 break;
2950 case VIK_UNITS_DISTANCE_MILES:
2951 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2952 break;
2953 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2954 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
2955 break;
2956 default:
2957 break;
2958 }
2959 return tmp_buf;
2960 }
2961 }
2962 break;
2963 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2964 {
2965 // Very simple tooltip - may expand detail in the future...
2966 static gchar tmp_buf[32];
2967 g_snprintf (tmp_buf, sizeof(tmp_buf),
2968 _("Waypoints: %d"),
2969 g_hash_table_size (l->waypoints));
2970 return tmp_buf;
2971 }
2972 break;
2973 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2974 {
2975 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2976 // NB It's OK to return NULL
2977 if ( w ) {
2978 if ( w->comment )
2979 return w->comment;
2980 else
2981 return w->description;
2982 }
2983 }
2984 break;
2985 default: break;
2986 }
2987 return NULL;
2988}
2989
2990#define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2991
2992/**
2993 * set_statusbar_msg_info_trkpt:
2994 *
2995 * Function to show track point information on the statusbar
2996 * Items displayed is controlled by the settings format code
2997 */
2998static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2999{
3000 gchar *statusbar_format_code = NULL;
3001 gboolean need2free = FALSE;
3002 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3003 // Otherwise use default
3004 statusbar_format_code = g_strdup ( "KEATDN" );
3005 need2free = TRUE;
3006 }
3007
3008 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
3009 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3010 g_free ( msg );
3011
3012 if ( need2free )
3013 g_free ( statusbar_format_code );
3014}
3015
3016/*
3017 * Function to show basic waypoint information on the statusbar
3018 */
3019static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3020{
3021 gchar tmp_buf1[64];
3022 switch (a_vik_get_units_height ()) {
3023 case VIK_UNITS_HEIGHT_FEET:
3024 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3025 break;
3026 default:
3027 //VIK_UNITS_HEIGHT_METRES:
3028 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3029 }
3030
3031 // Position part
3032 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3033 // one can easily use the current pointer position to see this if needed
3034 gchar *lat = NULL, *lon = NULL;
3035 static struct LatLon ll;
3036 vik_coord_to_latlon (&(wpt->coord), &ll);
3037 a_coords_latlon_to_string ( &ll, &lat, &lon );
3038
3039 // Combine parts to make overall message
3040 gchar *msg;
3041 if ( wpt->comment )
3042 // Add comment if available
3043 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3044 else
3045 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3046 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3047 g_free ( lat );
3048 g_free ( lon );
3049 g_free ( msg );
3050}
3051
3052/**
3053 * General layer selection function, find out which bit is selected and take appropriate action
3054 */
3055static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3056{
3057 // Reset
3058 l->current_wp = NULL;
3059 l->current_wp_id = NULL;
3060 trw_layer_cancel_current_tp ( l, FALSE );
3061
3062 // Clear statusbar
3063 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3064
3065 switch ( type )
3066 {
3067 case VIK_TREEVIEW_TYPE_LAYER:
3068 {
3069 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3070 /* Mark for redraw */
3071 return TRUE;
3072 }
3073 break;
3074
3075 case VIK_TREEVIEW_TYPE_SUBLAYER:
3076 {
3077 switch ( subtype )
3078 {
3079 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3080 {
3081 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3082 /* Mark for redraw */
3083 return TRUE;
3084 }
3085 break;
3086 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3087 {
3088 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3089 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3090 /* Mark for redraw */
3091 return TRUE;
3092 }
3093 break;
3094 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3095 {
3096 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3097 /* Mark for redraw */
3098 return TRUE;
3099 }
3100 break;
3101 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3102 {
3103 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3104 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3105 /* Mark for redraw */
3106 return TRUE;
3107 }
3108 break;
3109 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3110 {
3111 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3112 /* Mark for redraw */
3113 return TRUE;
3114 }
3115 break;
3116 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3117 {
3118 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3119 if ( wpt ) {
3120 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3121 // Show some waypoint info
3122 set_statusbar_msg_info_wpt ( l, wpt );
3123 /* Mark for redraw */
3124 return TRUE;
3125 }
3126 }
3127 break;
3128 default:
3129 {
3130 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3131 }
3132 break;
3133 }
3134 return FALSE;
3135 }
3136 break;
3137
3138 default:
3139 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3140 break;
3141 }
3142}
3143
3144GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3145{
3146 return l->tracks;
3147}
3148
3149GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3150{
3151 return l->routes;
3152}
3153
3154GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3155{
3156 return l->waypoints;
3157}
3158
3159GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3160{
3161 return vtl->tracks_iters;
3162}
3163
3164GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3165{
3166 return vtl->routes_iters;
3167}
3168
3169GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3170{
3171 return vtl->waypoints;
3172}
3173
3174gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3175{
3176 return ! ( g_hash_table_size ( vtl->tracks ) ||
3177 g_hash_table_size ( vtl->routes ) ||
3178 g_hash_table_size ( vtl->waypoints ) );
3179}
3180
3181gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3182{
3183 return vtl->tracks_visible;
3184}
3185
3186gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3187{
3188 return vtl->routes_visible;
3189}
3190
3191gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3192{
3193 return vtl->waypoints_visible;
3194}
3195
3196/*
3197 * ATM use a case sensitive find
3198 * Finds the first one
3199 */
3200static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3201{
3202 if ( wp && wp->name )
3203 if ( ! strcmp ( wp->name, name ) )
3204 return TRUE;
3205 return FALSE;
3206}
3207
3208/*
3209 * Get waypoint by name - not guaranteed to be unique
3210 * Finds the first one
3211 */
3212VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3213{
3214 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3215}
3216
3217/*
3218 * ATM use a case sensitive find
3219 * Finds the first one
3220 */
3221static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3222{
3223 if ( trk && trk->name )
3224 if ( ! strcmp ( trk->name, name ) )
3225 return TRUE;
3226 return FALSE;
3227}
3228
3229/*
3230 * Get track by name - not guaranteed to be unique
3231 * Finds the first one
3232 */
3233VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3234{
3235 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3236}
3237
3238/*
3239 * Get route by name - not guaranteed to be unique
3240 * Finds the first one
3241 */
3242VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3243{
3244 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3245}
3246
3247static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3248{
3249 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3250 maxmin[0].lat = trk->bbox.north;
3251 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3252 maxmin[1].lat = trk->bbox.south;
3253 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3254 maxmin[0].lon = trk->bbox.east;
3255 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3256 maxmin[1].lon = trk->bbox.west;
3257}
3258
3259static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3260{
3261 // Continually reuse maxmin to find the latest maximum and minimum values
3262 // First set to waypoints bounds
3263 maxmin[0].lat = vtl->waypoints_bbox.north;
3264 maxmin[1].lat = vtl->waypoints_bbox.south;
3265 maxmin[0].lon = vtl->waypoints_bbox.east;
3266 maxmin[1].lon = vtl->waypoints_bbox.west;
3267 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3268 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3269}
3270
3271gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3272{
3273 /* 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... */
3274 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3275 trw_layer_find_maxmin (vtl, maxmin);
3276 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3277 return FALSE;
3278 else
3279 {
3280 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3281 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3282 return TRUE;
3283 }
3284}
3285
3286static void trw_layer_centerize ( menu_array_layer values )
3287{
3288 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3289 VikCoord coord;
3290 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3291 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3292 else
3293 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3294}
3295
3296void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3297{
3298 /* First set the center [in case previously viewing from elsewhere] */
3299 /* Then loop through zoom levels until provided positions are in view */
3300 /* This method is not particularly fast - but should work well enough */
3301 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3302 VikCoord coord;
3303 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3304 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3305
3306 /* Convert into definite 'smallest' and 'largest' positions */
3307 struct LatLon minmin;
3308 if ( maxmin[0].lat < maxmin[1].lat )
3309 minmin.lat = maxmin[0].lat;
3310 else
3311 minmin.lat = maxmin[1].lat;
3312
3313 struct LatLon maxmax;
3314 if ( maxmin[0].lon > maxmin[1].lon )
3315 maxmax.lon = maxmin[0].lon;
3316 else
3317 maxmax.lon = maxmin[1].lon;
3318
3319 /* Never zoom in too far - generally not that useful, as too close ! */
3320 /* Always recalculate the 'best' zoom level */
3321 gdouble zoom = 1.0;
3322 vik_viewport_set_zoom ( vvp, zoom );
3323
3324 gdouble min_lat, max_lat, min_lon, max_lon;
3325 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3326 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3327 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3328 /* NB I think the logic used in this test to determine if the bounds is within view
3329 fails if track goes across 180 degrees longitude.
3330 Hopefully that situation is not too common...
3331 Mind you viking doesn't really do edge locations to well anyway */
3332 if ( min_lat < minmin.lat &&
3333 max_lat > minmin.lat &&
3334 min_lon < maxmax.lon &&
3335 max_lon > maxmax.lon )
3336 /* Found within zoom level */
3337 break;
3338
3339 /* Try next */
3340 zoom = zoom * 2;
3341 vik_viewport_set_zoom ( vvp, zoom );
3342 }
3343}
3344
3345gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3346{
3347 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3348 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3349 trw_layer_find_maxmin (vtl, maxmin);
3350 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3351 return FALSE;
3352 else {
3353 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3354 return TRUE;
3355 }
3356}
3357
3358static void trw_layer_auto_view ( menu_array_layer values )
3359{
3360 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3361 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3362 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3363 vik_layers_panel_emit_update ( vlp );
3364 }
3365 else
3366 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3367}
3368
3369static void trw_layer_export_gpspoint ( menu_array_layer values )
3370{
3371 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3372
3373 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3374
3375 g_free ( auto_save_name );
3376}
3377
3378static void trw_layer_export_gpsmapper ( menu_array_layer values )
3379{
3380 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3381
3382 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3383
3384 g_free ( auto_save_name );
3385}
3386
3387static void trw_layer_export_gpx ( menu_array_layer values )
3388{
3389 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3390
3391 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3392
3393 g_free ( auto_save_name );
3394}
3395
3396static void trw_layer_export_kml ( menu_array_layer values )
3397{
3398 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3399
3400 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3401
3402 g_free ( auto_save_name );
3403}
3404
3405static void trw_layer_export_geojson ( menu_array_layer values )
3406{
3407 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3408
3409 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3410
3411 g_free ( auto_save_name );
3412}
3413
3414static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3415{
3416 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3417 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3418}
3419
3420static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3421{
3422 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3423}
3424
3425static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3426{
3427 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3428}
3429
3430static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3431{
3432 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3433 VikTrack *trk;
3434 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3435 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3436 else
3437 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3438
3439 if ( !trk || !trk->name )
3440 return;
3441
3442 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3443
3444 gchar *label = NULL;
3445 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3446 label = _("Export Route as GPX");
3447 else
3448 label = _("Export Track as GPX");
3449 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3450
3451 g_free ( auto_save_name );
3452}
3453
3454gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3455{
3456 wpu_udata *user_data = udata;
3457 if ( wp == user_data->wp ) {
3458 user_data->uuid = id;
3459 return TRUE;
3460 }
3461 return FALSE;
3462}
3463
3464static void trw_layer_goto_wp ( menu_array_layer values )
3465{
3466 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3467 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3468 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3469 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3470 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3471 GTK_STOCK_CANCEL,
3472 GTK_RESPONSE_REJECT,
3473 GTK_STOCK_OK,
3474 GTK_RESPONSE_ACCEPT,
3475 NULL);
3476
3477 GtkWidget *label, *entry;
3478 label = gtk_label_new(_("Waypoint Name:"));
3479 entry = gtk_entry_new();
3480
3481 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3482 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3483 gtk_widget_show_all ( label );
3484 gtk_widget_show_all ( entry );
3485
3486 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3487
3488 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3489 {
3490 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3491 // Find *first* wp with the given name
3492 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3493
3494 if ( !wp )
3495 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3496 else
3497 {
3498 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3499 vik_layers_panel_emit_update ( vlp );
3500
3501 // Find and select on the side panel
3502 wpu_udata udata;
3503 udata.wp = wp;
3504 udata.uuid = NULL;
3505
3506 // Hmmm, want key of it
3507 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3508
3509 if ( wpf && udata.uuid ) {
3510 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3511 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3512 }
3513
3514 break;
3515 }
3516
3517 g_free ( name );
3518
3519 }
3520 gtk_widget_destroy ( dia );
3521}
3522
3523gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3524{
3525 gchar *default_name = highest_wp_number_get(vtl);
3526 VikWaypoint *wp = vik_waypoint_new();
3527 gchar *returned_name;
3528 gboolean updated;
3529 wp->coord = *def_coord;
3530
3531 // Attempt to auto set height if DEM data is available
3532 vik_waypoint_apply_dem_data ( wp, TRUE );
3533
3534 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3535
3536 if ( returned_name )
3537 {
3538 wp->visible = TRUE;
3539 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3540 g_free (default_name);
3541 g_free (returned_name);
3542 return TRUE;
3543 }
3544 g_free (default_name);
3545 vik_waypoint_free(wp);
3546 return FALSE;
3547}
3548
3549static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3550{
3551 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3552 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3553 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3554 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3555 VikViewport *vvp = vik_window_viewport(vw);
3556
3557 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3558 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3559 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3560 trw_layer_calculate_bounds_waypoints ( vtl );
3561 vik_layers_panel_emit_update ( vlp );
3562}
3563
3564static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3565{
3566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3567 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3568 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3569
3570 trw_layer_find_maxmin (vtl, maxmin);
3571 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3572 trw_layer_calculate_bounds_waypoints ( vtl );
3573 vik_layers_panel_emit_update ( vlp );
3574}
3575
3576#ifdef VIK_CONFIG_GEOTAG
3577static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3578{
3579 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3580 if ( wp )
3581 // Update directly - not changing the mtime
3582 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3583}
3584
3585static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3586{
3587 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3588 if ( wp )
3589 // Update directly
3590 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3591}
3592
3593/*
3594 * Use code in separate file for this feature as reasonably complex
3595 */
3596static void trw_layer_geotagging_track ( menu_array_sublayer values )
3597{
3598 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3599 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3600 // Unset so can be reverified later if necessary
3601 vtl->has_verified_thumbnails = FALSE;
3602
3603 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3604 vtl,
3605 NULL,
3606 track );
3607}
3608
3609static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3610{
3611 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3612 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3613
3614 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3615 vtl,
3616 wpt,
3617 NULL );
3618}
3619
3620static void trw_layer_geotagging ( menu_array_layer values )
3621{
3622 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3623 // Unset so can be reverified later if necessary
3624 vtl->has_verified_thumbnails = FALSE;
3625
3626 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3627 vtl,
3628 NULL,
3629 NULL );
3630}
3631#endif
3632
3633// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3634
3635static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3636{
3637 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3638 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3639 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3640 VikViewport *vvp = vik_window_viewport(vw);
3641
3642 vik_datasource_mode_t mode = datasource->mode;
3643 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3644 mode = VIK_DATASOURCE_ADDTOLAYER;
3645 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3646}
3647
3648/*
3649 * Acquire into this TRW Layer straight from GPS Device
3650 */
3651static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3652{
3653 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3654}
3655
3656/*
3657 * Acquire into this TRW Layer from Directions
3658 */
3659static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3660{
3661 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3662}
3663
3664/*
3665 * Acquire into this TRW Layer from an entered URL
3666 */
3667static void trw_layer_acquire_url_cb ( menu_array_layer values )
3668{
3669 trw_layer_acquire ( values, &vik_datasource_url_interface );
3670}
3671
3672#ifdef VIK_CONFIG_OPENSTREETMAP
3673/*
3674 * Acquire into this TRW Layer from OSM
3675 */
3676static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3677{
3678 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3679}
3680
3681/**
3682 * Acquire into this TRW Layer from OSM for 'My' Traces
3683 */
3684static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3685{
3686 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3687}
3688#endif
3689
3690#ifdef VIK_CONFIG_GEOCACHES
3691/*
3692 * Acquire into this TRW Layer from Geocaching.com
3693 */
3694static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3695{
3696 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3697}
3698#endif
3699
3700#ifdef VIK_CONFIG_GEOTAG
3701/*
3702 * Acquire into this TRW Layer from images
3703 */
3704static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3705{
3706 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3707
3708 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3709
3710 // Reverify thumbnails as they may have changed
3711 vtl->has_verified_thumbnails = FALSE;
3712 trw_layer_verify_thumbnails ( vtl, NULL );
3713}
3714#endif
3715
3716/*
3717 * Acquire into this TRW Layer from any GPS Babel supported file
3718 */
3719static void trw_layer_acquire_file_cb ( menu_array_layer values )
3720{
3721 trw_layer_acquire ( values, &vik_datasource_file_interface );
3722}
3723
3724static void trw_layer_gps_upload ( menu_array_layer values )
3725{
3726 menu_array_sublayer data;
3727 gint ii;
3728 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3729 data[ii] = NULL;
3730 data[MA_VTL] = values[MA_VTL];
3731 data[MA_VLP] = values[MA_VLP];
3732
3733 trw_layer_gps_upload_any ( data );
3734}
3735
3736/**
3737 * If pass_along[3] is defined that this will upload just that track
3738 */
3739static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3740{
3741 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3742 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3743
3744 // May not actually get a track here as values[2&3] can be null
3745 VikTrack *track = NULL;
3746 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3747 gboolean xfer_all = FALSE;
3748
3749 if ( values[MA_SUBTYPE] ) {
3750 xfer_all = FALSE;
3751 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3752 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3753 xfer_type = RTE;
3754 }
3755 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3756 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3757 xfer_type = TRK;
3758 }
3759 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3760 xfer_type = WPT;
3761 }
3762 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3763 xfer_type = RTE;
3764 }
3765 }
3766 else if ( !values[MA_CONFIRM] )
3767 xfer_all = TRUE; // i.e. whole layer
3768
3769 if (track && !track->visible) {
3770 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3771 return;
3772 }
3773
3774 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3775 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3776 GTK_DIALOG_DESTROY_WITH_PARENT,
3777 GTK_STOCK_OK,
3778 GTK_RESPONSE_ACCEPT,
3779 GTK_STOCK_CANCEL,
3780 GTK_RESPONSE_REJECT,
3781 NULL );
3782
3783 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3784 GtkWidget *response_w = NULL;
3785#if GTK_CHECK_VERSION (2, 20, 0)
3786 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3787#endif
3788
3789 if ( response_w )
3790 gtk_widget_grab_focus ( response_w );
3791
3792 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3793
3794 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3795 datasource_gps_clean_up ( dgs );
3796 gtk_widget_destroy ( dialog );
3797 return;
3798 }
3799
3800 // Get info from reused datasource dialog widgets
3801 gchar* protocol = datasource_gps_get_protocol ( dgs );
3802 gchar* port = datasource_gps_get_descriptor ( dgs );
3803 // NB don't free the above strings as they're references to values held elsewhere
3804 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3805 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3806 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3807 gboolean turn_off = datasource_gps_get_off ( dgs );
3808
3809 gtk_widget_destroy ( dialog );
3810
3811 // When called from the viewport - work the corresponding layerspanel:
3812 if ( !vlp ) {
3813 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3814 }
3815
3816 // Apply settings to transfer to the GPS device
3817 vik_gps_comm ( vtl,
3818 track,
3819 GPS_UP,
3820 protocol,
3821 port,
3822 FALSE,
3823 vik_layers_panel_get_viewport (vlp),
3824 vlp,
3825 do_tracks,
3826 do_routes,
3827 do_waypoints,
3828 turn_off );
3829}
3830
3831static void trw_layer_new_wp ( menu_array_layer values )
3832{
3833 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3834 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3835 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3836 instead return true if you want to update. */
3837 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 ) {
3838 trw_layer_calculate_bounds_waypoints ( vtl );
3839 vik_layers_panel_emit_update ( vlp );
3840 }
3841}
3842
3843static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3844{
3845 vtl->current_track = vik_track_new();
3846 vik_track_set_defaults ( vtl->current_track );
3847 vtl->current_track->visible = TRUE;
3848 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3849 // Create track with the preferred colour from the layer properties
3850 vtl->current_track->color = vtl->track_color;
3851 else
3852 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3853 vtl->current_track->has_color = TRUE;
3854 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3855}
3856
3857static void trw_layer_new_track ( menu_array_layer values )
3858{
3859 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3860
3861 if ( ! vtl->current_track ) {
3862 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3863 new_track_create_common ( vtl, name );
3864 g_free ( name );
3865
3866 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3867 }
3868}
3869
3870static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3871{
3872 vtl->current_track = vik_track_new();
3873 vik_track_set_defaults ( vtl->current_track );
3874 vtl->current_track->visible = TRUE;
3875 vtl->current_track->is_route = TRUE;
3876 // By default make all routes red
3877 vtl->current_track->has_color = TRUE;
3878 gdk_color_parse ( "red", &vtl->current_track->color );
3879 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3880}
3881
3882static void trw_layer_new_route ( menu_array_layer values )
3883{
3884 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3885
3886 if ( ! vtl->current_track ) {
3887 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3888 new_route_create_common ( vtl, name );
3889 g_free ( name );
3890 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3891 }
3892}
3893
3894static void trw_layer_auto_routes_view ( menu_array_layer values )
3895{
3896 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3897 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3898
3899 if ( g_hash_table_size (vtl->routes) > 0 ) {
3900 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3901 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3902 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3903 vik_layers_panel_emit_update ( vlp );
3904 }
3905}
3906
3907
3908static void trw_layer_finish_track ( menu_array_layer values )
3909{
3910 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3911 vtl->current_track = NULL;
3912 vtl->route_finder_started = FALSE;
3913 vik_layer_emit_update ( VIK_LAYER(vtl) );
3914}
3915
3916static void trw_layer_auto_tracks_view ( menu_array_layer values )
3917{
3918 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3919 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3920
3921 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3922 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3923 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3924 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3925 vik_layers_panel_emit_update ( vlp );
3926 }
3927}
3928
3929static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3930{
3931 /* NB do not care if wp is visible or not */
3932 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3933}
3934
3935static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3936{
3937 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3938 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3939
3940 /* Only 1 waypoint - jump straight to it */
3941 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3942 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3943 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3944 }
3945 /* If at least 2 waypoints - find center and then zoom to fit */
3946 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3947 {
3948 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3949 maxmin[0].lat = vtl->waypoints_bbox.north;
3950 maxmin[1].lat = vtl->waypoints_bbox.south;
3951 maxmin[0].lon = vtl->waypoints_bbox.east;
3952 maxmin[1].lon = vtl->waypoints_bbox.west;
3953 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3954 }
3955
3956 vik_layers_panel_emit_update ( vlp );
3957}
3958
3959void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3960{
3961 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3962}
3963
3964void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3965{
3966 if ( values[MA_MISC] ) {
3967 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3968 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3969 }
3970}
3971
3972static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3973{
3974 static menu_array_layer pass_along;
3975 GtkWidget *item;
3976 GtkWidget *export_submenu;
3977 pass_along[MA_VTL] = vtl;
3978 pass_along[MA_VLP] = vlp;
3979
3980 item = gtk_menu_item_new();
3981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3982 gtk_widget_show ( item );
3983
3984 if ( vtl->current_track ) {
3985 if ( vtl->current_track->is_route )
3986 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3987 else
3988 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3990 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3991 gtk_widget_show ( item );
3992
3993 // Add separator
3994 item = gtk_menu_item_new ();
3995 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3996 gtk_widget_show ( item );
3997 }
3998
3999 /* Now with icons */
4000 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4003 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4004 gtk_widget_show ( item );
4005
4006 GtkWidget *view_submenu = gtk_menu_new();
4007 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4010 gtk_widget_show ( item );
4011 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4012
4013 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4015 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4016 gtk_widget_show ( item );
4017
4018 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4020 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4021 gtk_widget_show ( item );
4022
4023 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4025 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4026 gtk_widget_show ( item );
4027
4028 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4031 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4032 gtk_widget_show ( item );
4033
4034 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4036 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4037 gtk_widget_show ( item );
4038
4039 export_submenu = gtk_menu_new ();
4040 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4042 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4043 gtk_widget_show ( item );
4044 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4045
4046 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4048 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4049 gtk_widget_show ( item );
4050
4051 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4053 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4054 gtk_widget_show ( item );
4055
4056 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4058 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4059 gtk_widget_show ( item );
4060
4061 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4063 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4064 gtk_widget_show ( item );
4065
4066 if ( have_geojson_export ) {
4067 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4069 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4070 gtk_widget_show ( item );
4071 }
4072
4073 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4075 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4076 gtk_widget_show ( item );
4077
4078 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4079 item = gtk_menu_item_new_with_mnemonic ( external1 );
4080 g_free ( external1 );
4081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4082 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4083 gtk_widget_show ( item );
4084
4085 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4086 item = gtk_menu_item_new_with_mnemonic ( external2 );
4087 g_free ( external2 );
4088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4089 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4090 gtk_widget_show ( item );
4091
4092 GtkWidget *new_submenu = gtk_menu_new();
4093 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4095 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4096 gtk_widget_show(item);
4097 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4098
4099 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4102 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4103 gtk_widget_show ( item );
4104
4105 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4108 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4109 gtk_widget_show ( item );
4110 // Make it available only when a new track *not* already in progress
4111 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4112
4113 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4117 gtk_widget_show ( item );
4118 // Make it available only when a new track *not* already in progress
4119 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4120
4121#ifdef VIK_CONFIG_GEOTAG
4122 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4124 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4125 gtk_widget_show ( item );
4126#endif
4127
4128 GtkWidget *acquire_submenu = gtk_menu_new ();
4129 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4131 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4132 gtk_widget_show ( item );
4133 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4134
4135 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4137 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4138 gtk_widget_show ( item );
4139
4140 /* FIXME: only add menu when at least a routing engine has support for Directions */
4141 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4144 gtk_widget_show ( item );
4145
4146#ifdef VIK_CONFIG_OPENSTREETMAP
4147 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4149 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4150 gtk_widget_show ( item );
4151
4152 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4155 gtk_widget_show ( item );
4156#endif
4157
4158 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4160 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4161 gtk_widget_show ( item );
4162
4163#ifdef VIK_CONFIG_GEONAMES
4164 GtkWidget *wikipedia_submenu = gtk_menu_new();
4165 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4167 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4168 gtk_widget_show(item);
4169 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4170
4171 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4174 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4175 gtk_widget_show ( item );
4176
4177 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4178 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4180 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4181 gtk_widget_show ( item );
4182#endif
4183
4184#ifdef VIK_CONFIG_GEOCACHES
4185 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4187 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4188 gtk_widget_show ( item );
4189#endif
4190
4191#ifdef VIK_CONFIG_GEOTAG
4192 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4193 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4194 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4195 gtk_widget_show ( item );
4196#endif
4197
4198 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4200 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4201 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4202 gtk_widget_show ( item );
4203
4204 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4205
4206 GtkWidget *upload_submenu = gtk_menu_new ();
4207 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4209 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4210 gtk_widget_show ( item );
4211 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4212
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4216 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4217 gtk_widget_show ( item );
4218
4219#ifdef VIK_CONFIG_OPENSTREETMAP
4220 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4223 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4224 gtk_widget_show ( item );
4225#endif
4226
4227 GtkWidget *delete_submenu = gtk_menu_new ();
4228 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4230 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4231 gtk_widget_show ( item );
4232 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4233
4234 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4235 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4237 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4238 gtk_widget_show ( item );
4239
4240 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4243 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4244 gtk_widget_show ( item );
4245
4246 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4247 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4249 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4250 gtk_widget_show ( item );
4251
4252 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4253 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4255 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4256 gtk_widget_show ( item );
4257
4258 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4261 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4262 gtk_widget_show ( item );
4263
4264 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4267 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4268 gtk_widget_show ( item );
4269
4270 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4271 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4272 if ( item ) {
4273 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4274 gtk_widget_show ( item );
4275 }
4276
4277 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4278 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4279 if ( item ) {
4280 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4281 gtk_widget_show ( item );
4282 }
4283
4284 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4287 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4288 gtk_widget_show ( item );
4289 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4290
4291 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4295 gtk_widget_show ( item );
4296 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4297}
4298
4299// Fake Waypoint UUIDs vi simple increasing integer
4300static guint wp_uuid = 0;
4301
4302void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4303{
4304 wp_uuid++;
4305
4306 vik_waypoint_set_name (wp, name);
4307
4308 if ( VIK_LAYER(vtl)->realized )
4309 {
4310 // Do we need to create the sublayer:
4311 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4312 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4313 }
4314
4315 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4316
4317 // Visibility column always needed for waypoints
4318 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
4319
4320 // Actual setting of visibility dependent on the waypoint
4321 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4322
4323 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4324
4325 // Sort now as post_read is not called on a realized waypoint
4326 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4327 }
4328
4329 highest_wp_number_add_wp(vtl, name);
4330 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4331
4332}
4333
4334// Fake Track UUIDs vi simple increasing integer
4335static guint tr_uuid = 0;
4336
4337void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4338{
4339 tr_uuid++;
4340
4341 vik_track_set_name (t, name);
4342
4343 if ( VIK_LAYER(vtl)->realized )
4344 {
4345 // Do we need to create the sublayer:
4346 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4347 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4348 }
4349
4350 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4351 // Visibility column always needed for tracks
4352 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
4353
4354 // Actual setting of visibility dependent on the track
4355 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4356
4357 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4358
4359 // Sort now as post_read is not called on a realized track
4360 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4361 }
4362
4363 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4364
4365 trw_layer_update_treeview ( vtl, t );
4366}
4367
4368// Fake Route UUIDs vi simple increasing integer
4369static guint rt_uuid = 0;
4370
4371void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4372{
4373 rt_uuid++;
4374
4375 vik_track_set_name (t, name);
4376
4377 if ( VIK_LAYER(vtl)->realized )
4378 {
4379 // Do we need to create the sublayer:
4380 if ( g_hash_table_size (vtl->routes) == 0 ) {
4381 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4382 }
4383
4384 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4385 // Visibility column always needed for routes
4386 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
4387 // Actual setting of visibility dependent on the route
4388 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4389
4390 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4391
4392 // Sort now as post_read is not called on a realized route
4393 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4394 }
4395
4396 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4397
4398 trw_layer_update_treeview ( vtl, t );
4399}
4400
4401/* to be called whenever a track has been deleted or may have been changed. */
4402void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4403{
4404 if (vtl->current_tp_track == trk )
4405 trw_layer_cancel_current_tp ( vtl, FALSE );
4406}
4407
4408/**
4409 * Normally this is done to due the waypoint size preference having changed
4410 */
4411void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4412{
4413 GHashTableIter iter;
4414 gpointer key, value;
4415
4416 // Foreach waypoint
4417 g_hash_table_iter_init ( &iter, vtl->waypoints );
4418 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4419 VikWaypoint *wp = VIK_WAYPOINT(value);
4420 if ( wp->symbol ) {
4421 // Reapply symbol setting to update the pixbuf
4422 gchar *tmp_symbol = g_strdup ( wp->symbol );
4423 vik_waypoint_set_symbol ( wp, tmp_symbol );
4424 g_free ( tmp_symbol );
4425 }
4426 }
4427}
4428
4429/**
4430 * trw_layer_new_unique_sublayer_name:
4431 *
4432 * Allocates a unique new name
4433 */
4434gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4435{
4436 gint i = 2;
4437 gchar *newname = g_strdup(name);
4438
4439 gpointer id = NULL;
4440 do {
4441 id = NULL;
4442 switch ( sublayer_type ) {
4443 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4444 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4445 break;
4446 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4447 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4448 break;
4449 default:
4450 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4451 break;
4452 }
4453 // If found a name already in use try adding 1 to it and we try again
4454 if ( id ) {
4455 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4456 g_free(newname);
4457 newname = new_newname;
4458 i++;
4459 }
4460 } while ( id != NULL);
4461
4462 return newname;
4463}
4464
4465void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4466{
4467 // No more uniqueness of name forced when loading from a file
4468 // This now makes this function a little redunant as we just flow the parameters through
4469 vik_trw_layer_add_waypoint ( vtl, name, wp );
4470}
4471
4472void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4473{
4474 if ( vtl->route_finder_append && vtl->current_track ) {
4475 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4476
4477 // enforce end of current track equal to start of tr
4478 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4479 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4480 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4481 vik_track_add_trackpoint ( vtl->current_track,
4482 vik_trackpoint_copy ( cur_end ),
4483 FALSE );
4484 }
4485
4486 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4487 vik_track_free ( tr );
4488 vtl->route_finder_append = FALSE; /* this means we have added it */
4489 } else {
4490
4491 // No more uniqueness of name forced when loading from a file
4492 if ( tr->is_route )
4493 vik_trw_layer_add_route ( vtl, name, tr );
4494 else
4495 vik_trw_layer_add_track ( vtl, name, tr );
4496
4497 if ( vtl->route_finder_check_added_track ) {
4498 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4499 vtl->route_finder_added_track = tr;
4500 }
4501 }
4502}
4503
4504static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4505{
4506 *l = g_list_append(*l, id);
4507}
4508
4509/*
4510 * Move an item from one TRW layer to another TRW layer
4511 */
4512static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4513{
4514 // TODO reconsider strategy when moving within layer (if anything...)
4515 gboolean rename = ( vtl_src != vtl_dest );
4516 if ( ! rename )
4517 return;
4518
4519 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4520 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4521
4522 gchar *newname;
4523 if ( rename )
4524 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4525 else
4526 newname = g_strdup ( trk->name );
4527
4528 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4529 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4530 g_free ( newname );
4531 vik_trw_layer_delete_track ( vtl_src, trk );
4532 }
4533
4534 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4535 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4536
4537 gchar *newname;
4538 if ( rename )
4539 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4540 else
4541 newname = g_strdup ( trk->name );
4542
4543 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4544 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4545 g_free ( newname );
4546 vik_trw_layer_delete_route ( vtl_src, trk );
4547 }
4548
4549 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4550 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4551
4552 gchar *newname;
4553 if ( rename )
4554 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4555 else
4556 newname = g_strdup ( wp->name );
4557
4558 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4559 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4560 g_free ( newname );
4561 trw_layer_delete_waypoint ( vtl_src, wp );
4562
4563 // Recalculate bounds even if not renamed as maybe dragged between layers
4564 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4565 trw_layer_calculate_bounds_waypoints ( vtl_src );
4566 }
4567}
4568
4569static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4570{
4571 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4572 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4573
4574 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4575 GList *items = NULL;
4576 GList *iter;
4577
4578 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4579 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4580 }
4581 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4582 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4583 }
4584 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4585 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4586 }
4587
4588 iter = items;
4589 while (iter) {
4590 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4591 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4592 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4593 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4594 } else {
4595 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4596 }
4597 iter = iter->next;
4598 }
4599 if (items)
4600 g_list_free(items);
4601 } else {
4602 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4603 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4604 }
4605}
4606
4607gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4608{
4609 trku_udata *user_data = udata;
4610 if ( trk == user_data->trk ) {
4611 user_data->uuid = id;
4612 return TRUE;
4613 }
4614 return FALSE;
4615}
4616
4617gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4618{
4619 gboolean was_visible = FALSE;
4620 if ( trk && trk->name ) {
4621
4622 if ( trk == vtl->current_track ) {
4623 vtl->current_track = NULL;
4624 vtl->current_tp_track = NULL;
4625 vtl->current_tp_id = NULL;
4626 vtl->moving_tp = FALSE;
4627 vtl->route_finder_started = FALSE;
4628 }
4629
4630 was_visible = trk->visible;
4631
4632 if ( trk == vtl->route_finder_added_track )
4633 vtl->route_finder_added_track = NULL;
4634
4635 trku_udata udata;
4636 udata.trk = trk;
4637 udata.uuid = NULL;
4638
4639 // Hmmm, want key of it
4640 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4641
4642 if ( trkf && udata.uuid ) {
4643 /* could be current_tp, so we have to check */
4644 trw_layer_cancel_tps_of_track ( vtl, trk );
4645
4646 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4647
4648 if ( it ) {
4649 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4650 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4651 g_hash_table_remove ( vtl->tracks, udata.uuid );
4652
4653 // If last sublayer, then remove sublayer container
4654 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4655 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4656 }
4657 }
4658 // Incase it was selected (no item delete signal ATM)
4659 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4660 }
4661 }
4662 return was_visible;
4663}
4664
4665gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4666{
4667 gboolean was_visible = FALSE;
4668
4669 if ( trk && trk->name ) {
4670
4671 if ( trk == vtl->current_track ) {
4672 vtl->current_track = NULL;
4673 vtl->current_tp_track = NULL;
4674 vtl->current_tp_id = NULL;
4675 vtl->moving_tp = FALSE;
4676 }
4677
4678 was_visible = trk->visible;
4679
4680 if ( trk == vtl->route_finder_added_track )
4681 vtl->route_finder_added_track = NULL;
4682
4683 trku_udata udata;
4684 udata.trk = trk;
4685 udata.uuid = NULL;
4686
4687 // Hmmm, want key of it
4688 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4689
4690 if ( trkf && udata.uuid ) {
4691 /* could be current_tp, so we have to check */
4692 trw_layer_cancel_tps_of_track ( vtl, trk );
4693
4694 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4695
4696 if ( it ) {
4697 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4698 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4699 g_hash_table_remove ( vtl->routes, udata.uuid );
4700
4701 // If last sublayer, then remove sublayer container
4702 if ( g_hash_table_size (vtl->routes) == 0 ) {
4703 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4704 }
4705 }
4706 // Incase it was selected (no item delete signal ATM)
4707 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4708 }
4709 }
4710 return was_visible;
4711}
4712
4713static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4714{
4715 gboolean was_visible = FALSE;
4716
4717 if ( wp && wp->name ) {
4718
4719 if ( wp == vtl->current_wp ) {
4720 vtl->current_wp = NULL;
4721 vtl->current_wp_id = NULL;
4722 vtl->moving_wp = FALSE;
4723 }
4724
4725 was_visible = wp->visible;
4726
4727 wpu_udata udata;
4728 udata.wp = wp;
4729 udata.uuid = NULL;
4730
4731 // Hmmm, want key of it
4732 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4733
4734 if ( wpf && udata.uuid ) {
4735 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4736
4737 if ( it ) {
4738 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4739 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4740
4741 highest_wp_number_remove_wp(vtl, wp->name);
4742 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4743
4744 // If last sublayer, then remove sublayer container
4745 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4746 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4747 }
4748 }
4749 // Incase it was selected (no item delete signal ATM)
4750 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4751 }
4752
4753 }
4754
4755 return was_visible;
4756}
4757
4758// Only for temporary use by trw_layer_delete_waypoint_by_name
4759static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4760{
4761 wpu_udata *user_data = udata;
4762 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4763 user_data->uuid = id;
4764 return TRUE;
4765 }
4766 return FALSE;
4767}
4768
4769/*
4770 * Delete a waypoint by the given name
4771 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4772 * as there be multiple waypoints with the same name
4773 */
4774static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4775{
4776 wpu_udata udata;
4777 // Fake a waypoint with the given name
4778 udata.wp = vik_waypoint_new ();
4779 vik_waypoint_set_name (udata.wp, name);
4780 // Currently only the name is used in this waypoint find function
4781 udata.uuid = NULL;
4782
4783 // Hmmm, want key of it
4784 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4785
4786 vik_waypoint_free (udata.wp);
4787
4788 if ( wpf && udata.uuid )
4789 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4790 else
4791 return FALSE;
4792}
4793
4794typedef struct {
4795 VikTrack *trk; // input
4796 gpointer uuid; // output
4797} tpu_udata;
4798
4799// Only for temporary use by trw_layer_delete_track_by_name
4800static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4801{
4802 tpu_udata *user_data = udata;
4803 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4804 user_data->uuid = id;
4805 return TRUE;
4806 }
4807 return FALSE;
4808}
4809
4810/*
4811 * Delete a track by the given name
4812 * NOTE: ATM this will delete the first encountered Track with the specified name
4813 * as there may be multiple tracks with the same name within the specified hash table
4814 */
4815static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4816{
4817 tpu_udata udata;
4818 // Fake a track with the given name
4819 udata.trk = vik_track_new ();
4820 vik_track_set_name (udata.trk, name);
4821 // Currently only the name is used in this waypoint find function
4822 udata.uuid = NULL;
4823
4824 // Hmmm, want key of it
4825 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4826
4827 vik_track_free (udata.trk);
4828
4829 if ( trkf && udata.uuid ) {
4830 // This could be a little better written...
4831 if ( vtl->tracks == ht_tracks )
4832 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4833 if ( vtl->routes == ht_tracks )
4834 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4835 return FALSE;
4836 }
4837 else
4838 return FALSE;
4839}
4840
4841static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4842{
4843 vik_treeview_item_delete (vt, it );
4844}
4845
4846void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4847{
4848
4849 vtl->current_track = NULL;
4850 vtl->route_finder_added_track = NULL;
4851 if (vtl->current_tp_track)
4852 trw_layer_cancel_current_tp(vtl, FALSE);
4853
4854 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4855 g_hash_table_remove_all(vtl->routes_iters);
4856 g_hash_table_remove_all(vtl->routes);
4857
4858 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4859
4860 vik_layer_emit_update ( VIK_LAYER(vtl) );
4861}
4862
4863void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4864{
4865
4866 vtl->current_track = NULL;
4867 vtl->route_finder_added_track = NULL;
4868 if (vtl->current_tp_track)
4869 trw_layer_cancel_current_tp(vtl, FALSE);
4870
4871 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4872 g_hash_table_remove_all(vtl->tracks_iters);
4873 g_hash_table_remove_all(vtl->tracks);
4874
4875 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4876
4877 vik_layer_emit_update ( VIK_LAYER(vtl) );
4878}
4879
4880void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4881{
4882 vtl->current_wp = NULL;
4883 vtl->current_wp_id = NULL;
4884 vtl->moving_wp = FALSE;
4885
4886 highest_wp_number_reset(vtl);
4887
4888 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4889 g_hash_table_remove_all(vtl->waypoints_iters);
4890 g_hash_table_remove_all(vtl->waypoints);
4891
4892 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4893
4894 vik_layer_emit_update ( VIK_LAYER(vtl) );
4895}
4896
4897static void trw_layer_delete_all_tracks ( menu_array_layer values )
4898{
4899 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4900 // Get confirmation from the user
4901 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4902 _("Are you sure you want to delete all tracks in %s?"),
4903 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4904 vik_trw_layer_delete_all_tracks (vtl);
4905}
4906
4907static void trw_layer_delete_all_routes ( menu_array_layer values )
4908{
4909 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4910 // Get confirmation from the user
4911 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4912 _("Are you sure you want to delete all routes in %s?"),
4913 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4914 vik_trw_layer_delete_all_routes (vtl);
4915}
4916
4917static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4918{
4919 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4920 // Get confirmation from the user
4921 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4922 _("Are you sure you want to delete all waypoints in %s?"),
4923 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4924 vik_trw_layer_delete_all_waypoints (vtl);
4925}
4926
4927static void trw_layer_delete_item ( menu_array_sublayer values )
4928{
4929 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4930 gboolean was_visible = FALSE;
4931 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4932 {
4933 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4934 if ( wp && wp->name ) {
4935 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4936 // Get confirmation from the user
4937 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4938 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4939 _("Are you sure you want to delete the waypoint \"%s\"?"),
4940 wp->name ) )
4941 return;
4942 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4943 trw_layer_calculate_bounds_waypoints ( vtl );
4944 }
4945 }
4946 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4947 {
4948 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4949 if ( trk && trk->name ) {
4950 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4951 // Get confirmation from the user
4952 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4953 _("Are you sure you want to delete the track \"%s\"?"),
4954 trk->name ) )
4955 return;
4956 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4957 }
4958 }
4959 else
4960 {
4961 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4962 if ( trk && trk->name ) {
4963 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4964 // Get confirmation from the user
4965 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4966 _("Are you sure you want to delete the route \"%s\"?"),
4967 trk->name ) )
4968 return;
4969 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4970 }
4971 }
4972 if ( was_visible )
4973 vik_layer_emit_update ( VIK_LAYER(vtl) );
4974}
4975
4976/**
4977 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4978 */
4979void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4980{
4981 vik_waypoint_set_name ( wp, new_name );
4982
4983 // Now update the treeview as well
4984 wpu_udata udataU;
4985 udataU.wp = wp;
4986 udataU.uuid = NULL;
4987
4988 // Need key of it for treeview update
4989 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4990
4991 if ( wpf && udataU.uuid ) {
4992 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4993
4994 if ( it ) {
4995 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4996 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4997 }
4998 }
4999}
5000
5001/**
5002 * Maintain icon of waypoint in the treeview
5003 */
5004void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5005{
5006 // update the treeview
5007 wpu_udata udataU;
5008 udataU.wp = wp;
5009 udataU.uuid = NULL;
5010
5011 // Need key of it for treeview update
5012 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5013
5014 if ( wpf && udataU.uuid ) {
5015 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5016
5017 if ( it ) {
5018 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5019 }
5020 }
5021}
5022
5023static void trw_layer_properties_item ( menu_array_sublayer values )
5024{
5025 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5026 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5027 {
5028 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5029
5030 if ( wp && wp->name )
5031 {
5032 gboolean updated = FALSE;
5033 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5034 if ( new_name )
5035 trw_layer_waypoint_rename ( vtl, wp, new_name );
5036
5037 if ( updated && values[MA_TV_ITER] )
5038 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5039
5040 if ( updated && VIK_LAYER(vtl)->visible )
5041 vik_layer_emit_update ( VIK_LAYER(vtl) );
5042 }
5043 }
5044 else
5045 {
5046 VikTrack *tr;
5047 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5048 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5049 else
5050 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5051
5052 if ( tr && tr->name )
5053 {
5054 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5055 vtl,
5056 tr,
5057 values[MA_VLP],
5058 values[MA_VVP],
5059 FALSE );
5060 }
5061 }
5062}
5063
5064/**
5065 * trw_layer_track_statistics:
5066 *
5067 * Show track statistics.
5068 * ATM jump to the stats page in the properties
5069 * TODO: consider separating the stats into an individual dialog?
5070 */
5071static void trw_layer_track_statistics ( menu_array_sublayer values )
5072{
5073 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5074 VikTrack *trk;
5075 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5076 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5077 else
5078 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5079
5080 if ( trk && trk->name ) {
5081 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5082 vtl,
5083 trk,
5084 values[MA_VLP],
5085 values[MA_VVP],
5086 TRUE );
5087 }
5088}
5089
5090/*
5091 * Update the treeview of the track id - primarily to update the icon
5092 */
5093void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5094{
5095 trku_udata udata;
5096 udata.trk = trk;
5097 udata.uuid = NULL;
5098
5099 gpointer *trkf = NULL;
5100 if ( trk->is_route )
5101 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5102 else
5103 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5104
5105 if ( trkf && udata.uuid ) {
5106
5107 GtkTreeIter *iter = NULL;
5108 if ( trk->is_route )
5109 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5110 else
5111 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5112
5113 if ( iter ) {
5114 // TODO: Make this a function
5115 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5116 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5117 ((trk->color.green & 0xff00) << 8) |
5118 (trk->color.blue & 0xff00);
5119 gdk_pixbuf_fill ( pixbuf, pixel );
5120 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5121 g_object_unref (pixbuf);
5122 }
5123
5124 }
5125}
5126
5127/*
5128 Parameter 1 -> VikLayersPanel
5129 Parameter 2 -> VikLayer
5130 Parameter 3 -> VikViewport
5131*/
5132static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5133{
5134 if ( vlp ) {
5135 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5136 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5137 }
5138 else {
5139 /* since vlp not set, vl & vvp should be valid instead! */
5140 if ( vl && vvp ) {
5141 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5142 vik_layer_emit_update ( VIK_LAYER(vl) );
5143 }
5144 }
5145}
5146
5147static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5148{
5149 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5150 VikTrack *track;
5151 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5152 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5153 else
5154 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5155
5156 if ( track && track->trackpoints )
5157 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5158}
5159
5160static void trw_layer_goto_track_center ( menu_array_sublayer values )
5161{
5162 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5163 VikTrack *track;
5164 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5165 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5166 else
5167 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5168
5169 if ( track && track->trackpoints )
5170 {
5171 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5172 VikCoord coord;
5173 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5174 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5175 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5176 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5177 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5178 }
5179}
5180
5181static void trw_layer_convert_track_route ( menu_array_sublayer values )
5182{
5183 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5184 VikTrack *trk;
5185 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5186 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5187 else
5188 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5189
5190 if ( !trk )
5191 return;
5192
5193 // Converting a track to a route can be a bit more complicated,
5194 // so give a chance to change our minds:
5195 if ( !trk->is_route &&
5196 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5197 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5198
5199 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5200 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5201 return;
5202}
5203
5204 // Copy it
5205 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5206
5207 // Convert
5208 trk_copy->is_route = !trk_copy->is_route;
5209
5210 // ATM can't set name to self - so must create temporary copy
5211 gchar *name = g_strdup ( trk_copy->name );
5212
5213 // Delete old one and then add new one
5214 if ( trk->is_route ) {
5215 vik_trw_layer_delete_route ( vtl, trk );
5216 vik_trw_layer_add_track ( vtl, name, trk_copy );
5217 }
5218 else {
5219 // Extra route conversion bits...
5220 vik_track_merge_segments ( trk_copy );
5221 vik_track_to_routepoints ( trk_copy );
5222
5223 vik_trw_layer_delete_track ( vtl, trk );
5224 vik_trw_layer_add_route ( vtl, name, trk_copy );
5225 }
5226 g_free ( name );
5227
5228 // Update in case color of track / route changes when moving between sublayers
5229 vik_layer_emit_update ( VIK_LAYER(vtl) );
5230}
5231
5232static void trw_layer_anonymize_times ( menu_array_sublayer values )
5233{
5234 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5235 VikTrack *track;
5236 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5237 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5238 else
5239 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5240
5241 if ( track )
5242 vik_track_anonymize_times ( track );
5243}
5244
5245static void trw_layer_extend_track_end ( menu_array_sublayer values )
5246{
5247 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5248 VikTrack *track;
5249 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5250 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5251 else
5252 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5253
5254 if ( !track )
5255 return;
5256
5257 vtl->current_track = track;
5258 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
5259
5260 if ( track->trackpoints )
5261 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5262}
5263
5264/**
5265 * extend a track using route finder
5266 */
5267static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5268{
5269 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5270 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5271 if ( !track )
5272 return;
5273
5274 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5275 vtl->current_track = track;
5276 vtl->route_finder_started = TRUE;
5277
5278 if ( track->trackpoints )
5279 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5280}
5281
5282/**
5283 *
5284 */
5285static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5286{
5287 // If have a vlp then perform a basic test to see if any DEM info available...
5288 if ( vlp ) {
5289 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5290
5291 if ( !g_list_length(dems) ) {
5292 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5293 return FALSE;
5294 }
5295 }
5296 return TRUE;
5297}
5298
5299/**
5300 * apply_dem_data_common:
5301 *
5302 * A common function for applying the DEM values and reporting the results.
5303 */
5304static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5305{
5306 if ( !trw_layer_dem_test ( vtl, vlp ) )
5307 return;
5308
5309 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5310 // Inform user how much was changed
5311 gchar str[64];
5312 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5313 g_snprintf(str, 64, tmp_str, changed);
5314 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5315}
5316
5317static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5318{
5319 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5320 VikTrack *track;
5321 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5322 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5323 else
5324 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5325
5326 if ( track )
5327 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5328}
5329
5330static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5331{
5332 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5333 VikTrack *track;
5334 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5335 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5336 else
5337 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5338
5339 if ( track )
5340 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5341}
5342
5343/**
5344 * smooth_it:
5345 *
5346 * A common function for applying the elevation smoothing and reporting the results.
5347 */
5348static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5349{
5350 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5351 // Inform user how much was changed
5352 gchar str[64];
5353 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5354 g_snprintf(str, 64, tmp_str, changed);
5355 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5356}
5357
5358/**
5359 *
5360 */
5361static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5362{
5363 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5364 VikTrack *track;
5365 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5366 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5367 else
5368 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5369
5370 if ( !track )
5371 return;
5372
5373 smooth_it ( vtl, track, FALSE );
5374}
5375
5376static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5377{
5378 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5379 VikTrack *track;
5380 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5381 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5382 else
5383 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5384
5385 if ( !track )
5386 return;
5387
5388 smooth_it ( vtl, track, TRUE );
5389}
5390
5391/**
5392 * Commonal helper function
5393 */
5394static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5395{
5396 gchar str[64];
5397 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5398 g_snprintf(str, 64, tmp_str, changed);
5399 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5400}
5401
5402static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5403{
5404 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5405 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5406
5407 if ( !trw_layer_dem_test ( vtl, vlp ) )
5408 return;
5409
5410 gint changed = 0;
5411 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5412 // Single Waypoint
5413 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5414 if ( wp )
5415 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5416 }
5417 else {
5418 // All waypoints
5419 GHashTableIter iter;
5420 gpointer key, value;
5421
5422 g_hash_table_iter_init ( &iter, vtl->waypoints );
5423 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5424 VikWaypoint *wp = VIK_WAYPOINT(value);
5425 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5426 }
5427 }
5428 wp_changed_message ( vtl, changed );
5429}
5430
5431static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5432{
5433 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5434 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5435
5436 if ( !trw_layer_dem_test ( vtl, vlp ) )
5437 return;
5438
5439 gint changed = 0;
5440 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5441 // Single Waypoint
5442 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5443 if ( wp )
5444 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5445 }
5446 else {
5447 // All waypoints
5448 GHashTableIter iter;
5449 gpointer key, value;
5450
5451 g_hash_table_iter_init ( &iter, vtl->waypoints );
5452 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5453 VikWaypoint *wp = VIK_WAYPOINT(value);
5454 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5455 }
5456 }
5457 wp_changed_message ( vtl, changed );
5458}
5459
5460static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5461{
5462 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5463 VikTrack *track;
5464 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5466 else
5467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5468
5469 if ( !track )
5470 return;
5471 if ( !track->trackpoints )
5472 return;
5473 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5474}
5475
5476static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5477{
5478 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5479 VikTrack *track;
5480 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5481 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5482 else
5483 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5484
5485 if ( !track )
5486 return;
5487
5488 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5489 if ( !vtp )
5490 return;
5491 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5492}
5493
5494static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5495{
5496 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5497 VikTrack *track;
5498 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5499 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5500 else
5501 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5502
5503 if ( !track )
5504 return;
5505
5506 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5507 if ( !vtp )
5508 return;
5509 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5510}
5511
5512static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5513{
5514 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5515 VikTrack *track;
5516 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5517 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5518 else
5519 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5520
5521 if ( !track )
5522 return;
5523
5524 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5525 if ( !vtp )
5526 return;
5527 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5528}
5529
5530/*
5531 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5532 */
5533static void trw_layer_auto_track_view ( menu_array_sublayer values )
5534{
5535 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5536 VikTrack *trk;
5537 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5538 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5539 else
5540 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5541
5542 if ( trk && trk->trackpoints )
5543 {
5544 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5545 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5546 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5547 if ( values[MA_VLP] )
5548 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5549 else
5550 vik_layer_emit_update ( VIK_LAYER(vtl) );
5551 }
5552}
5553
5554/*
5555 * Refine the selected track/route with a routing engine.
5556 * The routing engine is selected by the user, when requestiong the job.
5557 */
5558static void trw_layer_route_refine ( menu_array_sublayer values )
5559{
5560 static gint last_engine = 0;
5561 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5562 VikTrack *trk;
5563
5564 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5565 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5566 else
5567 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5568
5569 if ( trk && trk->trackpoints )
5570 {
5571 /* Check size of the route */
5572 int nb = vik_track_get_tp_count(trk);
5573 if (nb > 100) {
5574 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5575 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5576 GTK_MESSAGE_WARNING,
5577 GTK_BUTTONS_OK_CANCEL,
5578 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5579 nb);
5580 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5581 gtk_widget_destroy ( dialog );
5582 if (response != GTK_RESPONSE_OK )
5583 return;
5584 }
5585 /* Select engine from dialog */
5586 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5587 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5588 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5589 GTK_STOCK_CANCEL,
5590 GTK_RESPONSE_REJECT,
5591 GTK_STOCK_OK,
5592 GTK_RESPONSE_ACCEPT,
5593 NULL);
5594 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5595 gtk_widget_show_all(label);
5596
5597 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5598
5599 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5600 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5601 gtk_widget_show_all(combo);
5602
5603 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5604
5605 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5606
5607 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5608 {
5609 /* Dialog validated: retrieve selected engine and do the job */
5610 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5611 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5612
5613 /* Change cursor */
5614 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5615
5616 /* Force saving track */
5617 /* FIXME: remove or rename this hack */
5618 vtl->route_finder_check_added_track = TRUE;
5619
5620 /* the job */
5621 vik_routing_engine_refine (routing, vtl, trk);
5622
5623 /* FIXME: remove or rename this hack */
5624 if ( vtl->route_finder_added_track )
5625 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5626
5627 vtl->route_finder_added_track = NULL;
5628 vtl->route_finder_check_added_track = FALSE;
5629
5630 vik_layer_emit_update ( VIK_LAYER(vtl) );
5631
5632 /* Restore cursor */
5633 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5634 }
5635 gtk_widget_destroy ( dialog );
5636 }
5637}
5638
5639static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5640{
5641 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5642 trw_layer_tpwin_init ( vtl );
5643}
5644
5645/*************************************
5646 * merge/split by time routines
5647 *************************************/
5648
5649/* called for each key in track hash table.
5650 * If the current track has the same time stamp type, add it to the result,
5651 * except the one pointed by "exclude".
5652 * set exclude to NULL if there is no exclude to check.
5653 * Note that the result is in reverse (for performance reasons).
5654 */
5655typedef struct {
5656 GList **result;
5657 VikTrack *exclude;
5658 gboolean with_timestamps;
5659} twt_udata;
5660static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5661{
5662 twt_udata *user_data = udata;
5663 VikTrackpoint *p1, *p2;
5664 VikTrack *trk = VIK_TRACK(value);
5665 if (trk == user_data->exclude) {
5666 return;
5667 }
5668
5669 if (trk->trackpoints) {
5670 p1 = vik_track_get_tp_first(trk);
5671 p2 = vik_track_get_tp_last(trk);
5672
5673 if ( user_data->with_timestamps ) {
5674 if (!p1->has_timestamp || !p2->has_timestamp) {
5675 return;
5676 }
5677 }
5678 else {
5679 // Don't add tracks with timestamps when getting non timestamp tracks
5680 if (p1->has_timestamp || p2->has_timestamp) {
5681 return;
5682 }
5683 }
5684 }
5685
5686 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5687}
5688
5689/**
5690 * find_nearby_tracks_by_time:
5691 *
5692 * Called for each track in track hash table.
5693 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5694 * to the current track, then the current track is added to the list in user_data[0]
5695 */
5696static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5697{
5698 VikTrack *trk = VIK_TRACK(value);
5699
5700 GList **nearby_tracks = ((gpointer *)user_data)[0];
5701 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5702
5703 if ( !orig_trk || !orig_trk->trackpoints )
5704 return;
5705
5706 /* outline:
5707 * detect reasons for not merging, and return
5708 * if no reason is found not to merge, then do it.
5709 */
5710
5711 twt_udata *udata = user_data;
5712 // Exclude the original track from the compiled list
5713 if (trk == udata->exclude) {
5714 return;
5715 }
5716
5717 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5718 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5719
5720 if (trk->trackpoints) {
5721
5722 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5723 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5724
5725 if (!p1->has_timestamp || !p2->has_timestamp) {
5726 //g_print("no timestamp\n");
5727 return;
5728 }
5729
5730 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5731 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5732 if (! (abs(t1 - p2->timestamp) < threshold ||
5733 /* p1 p2 t1 t2 */
5734 abs(p1->timestamp - t2) < threshold)
5735 /* t1 t2 p1 p2 */
5736 ) {
5737 return;
5738 }
5739 }
5740
5741 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5742}
5743
5744/* comparison function used to sort tracks; a and b are hash table keys */
5745/* Not actively used - can be restored if needed
5746static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5747{
5748 GHashTable *tracks = user_data;
5749 time_t t1, t2;
5750
5751 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5752 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5753
5754 if (t1 < t2) return -1;
5755 if (t1 > t2) return 1;
5756 return 0;
5757}
5758*/
5759
5760/* comparison function used to sort trackpoints */
5761static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5762{
5763 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5764
5765 if (t1 < t2) return -1;
5766 if (t1 > t2) return 1;
5767 return 0;
5768}
5769
5770/**
5771 * comparison function which can be used to sort tracks or waypoints by name
5772 */
5773static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5774{
5775 const gchar* namea = (const gchar*) a;
5776 const gchar* nameb = (const gchar*) b;
5777 if ( namea == NULL || nameb == NULL)
5778 return 0;
5779 else
5780 // Same sort method as used in the vik_treeview_*_alphabetize functions
5781 return strcmp ( namea, nameb );
5782}
5783
5784/**
5785 * Attempt to merge selected track with other tracks specified by the user
5786 * Tracks to merge with must be of the same 'type' as the selected track -
5787 * either all with timestamps, or all without timestamps
5788 */
5789static void trw_layer_merge_with_other ( menu_array_sublayer values )
5790{
5791 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5792 GList *other_tracks = NULL;
5793 GHashTable *ght_tracks;
5794 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5795 ght_tracks = vtl->routes;
5796 else
5797 ght_tracks = vtl->tracks;
5798
5799 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5800
5801 if ( !track )
5802 return;
5803
5804 if ( !track->trackpoints )
5805 return;
5806
5807 twt_udata udata;
5808 udata.result = &other_tracks;
5809 udata.exclude = track;
5810 // Allow merging with 'similar' time type time tracks
5811 // i.e. either those times, or those without
5812 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5813
5814 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5815 other_tracks = g_list_reverse(other_tracks);
5816
5817 if ( !other_tracks ) {
5818 if ( udata.with_timestamps )
5819 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5820 else
5821 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5822 return;
5823 }
5824
5825 // Sort alphabetically for user presentation
5826 // Convert into list of names for usage with dialog function
5827 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5828 GList *other_tracks_names = NULL;
5829 GList *iter = g_list_first ( other_tracks );
5830 while ( iter ) {
5831 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5832 iter = g_list_next ( iter );
5833 }
5834
5835 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5836
5837 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5838 other_tracks_names,
5839 TRUE,
5840 _("Merge with..."),
5841 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5842 g_list_free(other_tracks);
5843 g_list_free(other_tracks_names);
5844
5845 if (merge_list)
5846 {
5847 GList *l;
5848 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5849 VikTrack *merge_track;
5850 if ( track->is_route )
5851 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5852 else
5853 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5854
5855 if (merge_track) {
5856 vik_track_steal_and_append_trackpoints ( track, merge_track );
5857 if ( track->is_route )
5858 vik_trw_layer_delete_route (vtl, merge_track);
5859 else
5860 vik_trw_layer_delete_track (vtl, merge_track);
5861 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5862 }
5863 }
5864 for (l = merge_list; l != NULL; l = g_list_next(l))
5865 g_free(l->data);
5866 g_list_free(merge_list);
5867
5868 vik_layer_emit_update( VIK_LAYER(vtl) );
5869 }
5870}
5871
5872// c.f. trw_layer_sorted_track_id_by_name_list
5873// but don't add the specified track to the list (normally current track)
5874static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5875{
5876 twt_udata *user_data = udata;
5877
5878 // Skip self
5879 if (trk == user_data->exclude) {
5880 return;
5881 }
5882
5883 // Sort named list alphabetically
5884 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5885}
5886
5887/**
5888 * Join - this allows combining 'tracks' and 'track routes'
5889 * i.e. doesn't care about whether tracks have consistent timestamps
5890 * ATM can only append one track at a time to the currently selected track
5891 */
5892static void trw_layer_append_track ( menu_array_sublayer values )
5893{
5894
5895 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5896 VikTrack *trk;
5897 GHashTable *ght_tracks;
5898 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5899 ght_tracks = vtl->routes;
5900 else
5901 ght_tracks = vtl->tracks;
5902
5903 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5904
5905 if ( !trk )
5906 return;
5907
5908 GList *other_tracks_names = NULL;
5909
5910 // Sort alphabetically for user presentation
5911 // Convert into list of names for usage with dialog function
5912 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5913 twt_udata udata;
5914 udata.result = &other_tracks_names;
5915 udata.exclude = trk;
5916
5917 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5918
5919 // Note the limit to selecting one track only
5920 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5921 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5922 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5923 other_tracks_names,
5924 FALSE,
5925 trk->is_route ? _("Append Route"): _("Append Track"),
5926 trk->is_route ? _("Select the route to append after the current route") :
5927 _("Select the track to append after the current track") );
5928
5929 g_list_free(other_tracks_names);
5930
5931 // It's a list, but shouldn't contain more than one other track!
5932 if ( append_list ) {
5933 GList *l;
5934 for (l = append_list; l != NULL; l = g_list_next(l)) {
5935 // TODO: at present this uses the first track found by name,
5936 // which with potential multiple same named tracks may not be the one selected...
5937 VikTrack *append_track;
5938 if ( trk->is_route )
5939 append_track = vik_trw_layer_get_route ( vtl, l->data );
5940 else
5941 append_track = vik_trw_layer_get_track ( vtl, l->data );
5942
5943 if ( append_track ) {
5944 vik_track_steal_and_append_trackpoints ( trk, append_track );
5945 if ( trk->is_route )
5946 vik_trw_layer_delete_route (vtl, append_track);
5947 else
5948 vik_trw_layer_delete_track (vtl, append_track);
5949 }
5950 }
5951 for (l = append_list; l != NULL; l = g_list_next(l))
5952 g_free(l->data);
5953 g_list_free(append_list);
5954
5955 vik_layer_emit_update( VIK_LAYER(vtl) );
5956 }
5957}
5958
5959/**
5960 * Very similar to trw_layer_append_track for joining
5961 * but this allows selection from the 'other' list
5962 * If a track is selected, then is shows routes and joins the selected one
5963 * If a route is selected, then is shows tracks and joins the selected one
5964 */
5965static void trw_layer_append_other ( menu_array_sublayer values )
5966{
5967
5968 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5969 VikTrack *trk;
5970 GHashTable *ght_mykind, *ght_others;
5971 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5972 ght_mykind = vtl->routes;
5973 ght_others = vtl->tracks;
5974 }
5975 else {
5976 ght_mykind = vtl->tracks;
5977 ght_others = vtl->routes;
5978 }
5979
5980 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5981
5982 if ( !trk )
5983 return;
5984
5985 GList *other_tracks_names = NULL;
5986
5987 // Sort alphabetically for user presentation
5988 // Convert into list of names for usage with dialog function
5989 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5990 twt_udata udata;
5991 udata.result = &other_tracks_names;
5992 udata.exclude = trk;
5993
5994 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5995
5996 // Note the limit to selecting one track only
5997 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5998 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5999 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6000 other_tracks_names,
6001 FALSE,
6002 trk->is_route ? _("Append Track"): _("Append Route"),
6003 trk->is_route ? _("Select the track to append after the current route") :
6004 _("Select the route to append after the current track") );
6005
6006 g_list_free(other_tracks_names);
6007
6008 // It's a list, but shouldn't contain more than one other track!
6009 if ( append_list ) {
6010 GList *l;
6011 for (l = append_list; l != NULL; l = g_list_next(l)) {
6012 // TODO: at present this uses the first track found by name,
6013 // which with potential multiple same named tracks may not be the one selected...
6014
6015 // Get FROM THE OTHER TYPE list
6016 VikTrack *append_track;
6017 if ( trk->is_route )
6018 append_track = vik_trw_layer_get_track ( vtl, l->data );
6019 else
6020 append_track = vik_trw_layer_get_route ( vtl, l->data );
6021
6022 if ( append_track ) {
6023
6024 if ( !append_track->is_route &&
6025 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6026 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6027
6028 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6029 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6030 vik_track_merge_segments ( append_track );
6031 vik_track_to_routepoints ( append_track );
6032 }
6033 else {
6034 break;
6035 }
6036 }
6037
6038 vik_track_steal_and_append_trackpoints ( trk, append_track );
6039
6040 // Delete copied which is FROM THE OTHER TYPE list
6041 if ( trk->is_route )
6042 vik_trw_layer_delete_track (vtl, append_track);
6043 else
6044 vik_trw_layer_delete_route (vtl, append_track);
6045 }
6046 }
6047 for (l = append_list; l != NULL; l = g_list_next(l))
6048 g_free(l->data);
6049 g_list_free(append_list);
6050 vik_layer_emit_update( VIK_LAYER(vtl) );
6051 }
6052}
6053
6054/* merge by segments */
6055static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6056{
6057 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6058 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6059 guint segments = vik_track_merge_segments ( trk );
6060 // NB currently no need to redraw as segments not actually shown on the display
6061 // However inform the user of what happened:
6062 gchar str[64];
6063 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6064 g_snprintf(str, 64, tmp_str, segments);
6065 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6066}
6067
6068/* merge by time routine */
6069static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6070{
6071 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6072
6073 //time_t t1, t2;
6074
6075 GList *tracks_with_timestamp = NULL;
6076 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6077 if (orig_trk->trackpoints &&
6078 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6079 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6080 return;
6081 }
6082
6083 twt_udata udata;
6084 udata.result = &tracks_with_timestamp;
6085 udata.exclude = orig_trk;
6086 udata.with_timestamps = TRUE;
6087 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6088 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6089
6090 if (!tracks_with_timestamp) {
6091 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6092 return;
6093 }
6094 g_list_free(tracks_with_timestamp);
6095
6096 static guint threshold_in_minutes = 1;
6097 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6098 _("Merge Threshold..."),
6099 _("Merge when time between tracks less than:"),
6100 &threshold_in_minutes)) {
6101 return;
6102 }
6103
6104 // keep attempting to merge all tracks until no merges within the time specified is possible
6105 gboolean attempt_merge = TRUE;
6106 GList *nearby_tracks = NULL;
6107 GList *trps;
6108 static gpointer params[3];
6109
6110 while ( attempt_merge ) {
6111
6112 // Don't try again unless tracks have changed
6113 attempt_merge = FALSE;
6114
6115 trps = orig_trk->trackpoints;
6116 if ( !trps )
6117 return;
6118
6119 if (nearby_tracks) {
6120 g_list_free(nearby_tracks);
6121 nearby_tracks = NULL;
6122 }
6123
6124 params[0] = &nearby_tracks;
6125 params[1] = orig_trk;
6126 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6127
6128 /* get a list of adjacent-in-time tracks */
6129 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6130
6131 /* merge them */
6132 GList *l = nearby_tracks;
6133 while ( l ) {
6134 /* remove trackpoints from merged track, delete track */
6135 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6136 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6137
6138 // Tracks have changed, therefore retry again against all the remaining tracks
6139 attempt_merge = TRUE;
6140
6141 l = g_list_next(l);
6142 }
6143
6144 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6145 }
6146
6147 g_list_free(nearby_tracks);
6148
6149 vik_layer_emit_update( VIK_LAYER(vtl) );
6150}
6151
6152/**
6153 * Split a track at the currently selected trackpoint
6154 */
6155static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6156{
6157 if ( !vtl->current_tpl )
6158 return;
6159
6160 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6161 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6162 if ( name ) {
6163 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6164 GList *newglist = g_list_alloc ();
6165 newglist->prev = NULL;
6166 newglist->next = vtl->current_tpl->next;
6167 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6168 tr->trackpoints = newglist;
6169
6170 vtl->current_tpl->next->prev = newglist; /* end old track here */
6171 vtl->current_tpl->next = NULL;
6172
6173 // Bounds of the selected track changed due to the split
6174 vik_track_calculate_bounds ( vtl->current_tp_track );
6175
6176 vtl->current_tpl = newglist; /* change tp to first of new track. */
6177 vtl->current_tp_track = tr;
6178
6179 if ( tr->is_route )
6180 vik_trw_layer_add_route ( vtl, name, tr );
6181 else
6182 vik_trw_layer_add_track ( vtl, name, tr );
6183
6184 // Bounds of the new track created by the split
6185 vik_track_calculate_bounds ( tr );
6186
6187 trku_udata udata;
6188 udata.trk = tr;
6189 udata.uuid = NULL;
6190
6191 // Also need id of newly created track
6192 gpointer *trkf;
6193 if ( tr->is_route )
6194 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6195 else
6196 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6197
6198 if ( trkf && udata.uuid )
6199 vtl->current_tp_id = udata.uuid;
6200 else
6201 vtl->current_tp_id = NULL;
6202
6203 vik_layer_emit_update(VIK_LAYER(vtl));
6204 }
6205 g_free ( name );
6206 }
6207}
6208
6209/* split by time routine */
6210static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6211{
6212 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6213 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6214 GList *trps = track->trackpoints;
6215 GList *iter;
6216 GList *newlists = NULL;
6217 GList *newtps = NULL;
6218 static guint thr = 1;
6219
6220 time_t ts, prev_ts;
6221
6222 if ( !trps )
6223 return;
6224
6225 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6226 _("Split Threshold..."),
6227 _("Split when time between trackpoints exceeds:"),
6228 &thr)) {
6229 return;
6230 }
6231
6232 /* iterate through trackpoints, and copy them into new lists without touching original list */
6233 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6234 iter = trps;
6235
6236 while (iter) {
6237 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6238
6239 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6240 if (ts < prev_ts) {
6241 gchar tmp_str[64];
6242 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6243 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6244 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6245 tmp_str ) ) {
6246 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6247 }
6248 return;
6249 }
6250
6251 if (ts - prev_ts > thr*60) {
6252 /* flush accumulated trackpoints into new list */
6253 newlists = g_list_append(newlists, g_list_reverse(newtps));
6254 newtps = NULL;
6255 }
6256
6257 /* accumulate trackpoint copies in newtps, in reverse order */
6258 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6259 prev_ts = ts;
6260 iter = g_list_next(iter);
6261 }
6262 if (newtps) {
6263 newlists = g_list_append(newlists, g_list_reverse(newtps));
6264 }
6265
6266 /* put lists of trackpoints into tracks */
6267 iter = newlists;
6268 // Only bother updating if the split results in new tracks
6269 if (g_list_length (newlists) > 1) {
6270 while (iter) {
6271 gchar *new_tr_name;
6272 VikTrack *tr;
6273
6274 tr = vik_track_copy ( track, FALSE );
6275 tr->trackpoints = (GList *)(iter->data);
6276
6277 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6278 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6279 g_free ( new_tr_name );
6280 vik_track_calculate_bounds ( tr );
6281 iter = g_list_next(iter);
6282 }
6283 // Remove original track and then update the display
6284 vik_trw_layer_delete_track (vtl, track);
6285 vik_layer_emit_update(VIK_LAYER(vtl));
6286 }
6287 g_list_free(newlists);
6288}
6289
6290/**
6291 * Split a track by the number of points as specified by the user
6292 */
6293static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6294{
6295 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6296 VikTrack *track;
6297 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6298 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6299 else
6300 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6301
6302 if ( !track )
6303 return;
6304
6305 // Check valid track
6306 GList *trps = track->trackpoints;
6307 if ( !trps )
6308 return;
6309
6310 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6311 _("Split Every Nth Point"),
6312 _("Split on every Nth point:"),
6313 250, // Default value as per typical limited track capacity of various GPS devices
6314 2, // Min
6315 65536, // Max
6316 5); // Step
6317 // Was a valid number returned?
6318 if (!points)
6319 return;
6320
6321 // Now split...
6322 GList *iter;
6323 GList *newlists = NULL;
6324 GList *newtps = NULL;
6325 gint count = 0;
6326 iter = trps;
6327
6328 while (iter) {
6329 /* accumulate trackpoint copies in newtps, in reverse order */
6330 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6331 count++;
6332 if (count >= points) {
6333 /* flush accumulated trackpoints into new list */
6334 newlists = g_list_append(newlists, g_list_reverse(newtps));
6335 newtps = NULL;
6336 count = 0;
6337 }
6338 iter = g_list_next(iter);
6339 }
6340
6341 // If there is a remaining chunk put that into the new split list
6342 // This may well be the whole track if no split points were encountered
6343 if (newtps) {
6344 newlists = g_list_append(newlists, g_list_reverse(newtps));
6345 }
6346
6347 /* put lists of trackpoints into tracks */
6348 iter = newlists;
6349 // Only bother updating if the split results in new tracks
6350 if (g_list_length (newlists) > 1) {
6351 while (iter) {
6352 gchar *new_tr_name;
6353 VikTrack *tr;
6354
6355 tr = vik_track_copy ( track, FALSE );
6356 tr->trackpoints = (GList *)(iter->data);
6357
6358 if ( track->is_route ) {
6359 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6360 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6361 }
6362 else {
6363 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6364 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6365 }
6366 g_free ( new_tr_name );
6367 vik_track_calculate_bounds ( tr );
6368
6369 iter = g_list_next(iter);
6370 }
6371 // Remove original track and then update the display
6372 if ( track->is_route )
6373 vik_trw_layer_delete_route (vtl, track);
6374 else
6375 vik_trw_layer_delete_track (vtl, track);
6376 vik_layer_emit_update(VIK_LAYER(vtl));
6377 }
6378 g_list_free(newlists);
6379}
6380
6381/**
6382 * Split a track at the currently selected trackpoint
6383 */
6384static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6385{
6386 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6387 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6388 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6389}
6390
6391/**
6392 * Split a track by its segments
6393 * Routes do not have segments so don't call this for routes
6394 */
6395static void trw_layer_split_segments ( menu_array_sublayer values )
6396{
6397 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6398 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6399
6400 if ( !trk )
6401 return;
6402
6403 guint ntracks;
6404
6405 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6406 gchar *new_tr_name;
6407 guint i;
6408 for ( i = 0; i < ntracks; i++ ) {
6409 if ( tracks[i] ) {
6410 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6411 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6412 g_free ( new_tr_name );
6413 }
6414 }
6415 if ( tracks ) {
6416 g_free ( tracks );
6417 // Remove original track
6418 vik_trw_layer_delete_track ( vtl, trk );
6419 vik_layer_emit_update ( VIK_LAYER(vtl) );
6420 }
6421 else {
6422 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6423 }
6424}
6425/* end of split/merge routines */
6426
6427static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6428{
6429 GList *new_tpl;
6430
6431 // Find available adjacent trackpoint
6432 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6433 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6434 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6435
6436 // Delete current trackpoint
6437 vik_trackpoint_free ( vtl->current_tpl->data );
6438 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6439
6440 // Set to current to the available adjacent trackpoint
6441 vtl->current_tpl = new_tpl;
6442
6443 if ( vtl->current_tp_track ) {
6444 vik_track_calculate_bounds ( vtl->current_tp_track );
6445 }
6446 }
6447 else {
6448 // Delete current trackpoint
6449 vik_trackpoint_free ( vtl->current_tpl->data );
6450 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6451 trw_layer_cancel_current_tp ( vtl, FALSE );
6452 }
6453}
6454
6455/**
6456 * Delete the selected point
6457 */
6458static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6459{
6460 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6461 VikTrack *trk;
6462 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6463 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6464 else
6465 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6466
6467 if ( !trk )
6468 return;
6469
6470 if ( !vtl->current_tpl )
6471 return;
6472
6473 trw_layer_trackpoint_selected_delete ( vtl, trk );
6474
6475 // Track has been updated so update tps:
6476 trw_layer_cancel_tps_of_track ( vtl, trk );
6477
6478 vik_layer_emit_update ( VIK_LAYER(vtl) );
6479}
6480
6481/**
6482 * Delete adjacent track points at the same position
6483 * AKA Delete Dulplicates on the Properties Window
6484 */
6485static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6486{
6487 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6488 VikTrack *trk;
6489 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6490 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6491 else
6492 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6493
6494 if ( !trk )
6495 return;
6496
6497 gulong removed = vik_track_remove_dup_points ( trk );
6498
6499 // Track has been updated so update tps:
6500 trw_layer_cancel_tps_of_track ( vtl, trk );
6501
6502 // Inform user how much was deleted as it's not obvious from the normal view
6503 gchar str[64];
6504 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6505 g_snprintf(str, 64, tmp_str, removed);
6506 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6507
6508 vik_layer_emit_update ( VIK_LAYER(vtl) );
6509}
6510
6511/**
6512 * Delete adjacent track points with the same timestamp
6513 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6514 */
6515static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6516{
6517 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6518 VikTrack *trk;
6519 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6520 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6521 else
6522 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6523
6524 if ( !trk )
6525 return;
6526
6527 gulong removed = vik_track_remove_same_time_points ( trk );
6528
6529 // Track has been updated so update tps:
6530 trw_layer_cancel_tps_of_track ( vtl, trk );
6531
6532 // Inform user how much was deleted as it's not obvious from the normal view
6533 gchar str[64];
6534 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6535 g_snprintf(str, 64, tmp_str, removed);
6536 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6537
6538 vik_layer_emit_update ( VIK_LAYER(vtl) );
6539}
6540
6541/**
6542 * Insert a point
6543 */
6544static void trw_layer_insert_point_after ( menu_array_sublayer values )
6545{
6546 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6547 VikTrack *track;
6548 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6549 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6550 else
6551 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6552
6553 if ( ! track )
6554 return;
6555
6556 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6557
6558 vik_layer_emit_update ( VIK_LAYER(vtl) );
6559}
6560
6561static void trw_layer_insert_point_before ( menu_array_sublayer values )
6562{
6563 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6564 VikTrack *track;
6565 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6566 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6567 else
6568 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6569
6570 if ( ! track )
6571 return;
6572
6573 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6574
6575 vik_layer_emit_update ( VIK_LAYER(vtl) );
6576}
6577
6578/**
6579 * Reverse a track
6580 */
6581static void trw_layer_reverse ( menu_array_sublayer values )
6582{
6583 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6584 VikTrack *track;
6585 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6586 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6587 else
6588 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6589
6590 if ( ! track )
6591 return;
6592
6593 vik_track_reverse ( track );
6594
6595 vik_layer_emit_update ( VIK_LAYER(vtl) );
6596}
6597
6598/**
6599 * Open a diary at the specified date
6600 */
6601static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6602{
6603 GError *err = NULL;
6604 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6605 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6606 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6607 g_error_free ( err );
6608 }
6609 g_free ( cmd );
6610}
6611
6612/**
6613 * Open a diary at the date of the track or waypoint
6614 */
6615static void trw_layer_diary ( menu_array_sublayer values )
6616{
6617 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6618
6619 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6620 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6621 if ( ! trk )
6622 return;
6623
6624 gchar date_buf[20];
6625 date_buf[0] = '\0';
6626 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6627 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6628 trw_layer_diary_open ( vtl, date_buf );
6629 }
6630 else
6631 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6632 }
6633 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6634 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6635 if ( ! wpt )
6636 return;
6637
6638 gchar date_buf[20];
6639 date_buf[0] = '\0';
6640 if ( wpt->has_timestamp ) {
6641 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6642 trw_layer_diary_open ( vtl, date_buf );
6643 }
6644 else
6645 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6646 }
6647}
6648
6649/**
6650 * Similar to trw_layer_enum_item, but this uses a sorted method
6651 */
6652/* Currently unused
6653static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6654{
6655 GList **list = (GList**)udata;
6656 // *list = g_list_prepend(*all, key); //unsorted method
6657 // Sort named list alphabetically
6658 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6659}
6660*/
6661
6662/**
6663 * Now Waypoint specific sort
6664 */
6665static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6666{
6667 GList **list = (GList**)udata;
6668 // Sort named list alphabetically
6669 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6670}
6671
6672/**
6673 * Track specific sort
6674 */
6675static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6676{
6677 GList **list = (GList**)udata;
6678 // Sort named list alphabetically
6679 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6680}
6681
6682
6683typedef struct {
6684 gboolean has_same_track_name;
6685 const gchar *same_track_name;
6686} same_track_name_udata;
6687
6688static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6689{
6690 const gchar* namea = (const gchar*) aa;
6691 const gchar* nameb = (const gchar*) bb;
6692
6693 // the test
6694 gint result = strcmp ( namea, nameb );
6695
6696 if ( result == 0 ) {
6697 // Found two names the same
6698 same_track_name_udata *user_data = udata;
6699 user_data->has_same_track_name = TRUE;
6700 user_data->same_track_name = namea;
6701 }
6702
6703 // Leave ordering the same
6704 return 0;
6705}
6706
6707/**
6708 * Find out if any tracks have the same name in this hash table
6709 */
6710static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6711{
6712 // Sort items by name, then compare if any next to each other are the same
6713
6714 GList *track_names = NULL;
6715 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6716
6717 // No tracks
6718 if ( ! track_names )
6719 return FALSE;
6720
6721 same_track_name_udata udata;
6722 udata.has_same_track_name = FALSE;
6723
6724 // Use sort routine to traverse list comparing items
6725 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6726 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6727 // Still no tracks...
6728 if ( ! dummy_list )
6729 return FALSE;
6730
6731 return udata.has_same_track_name;
6732}
6733
6734/**
6735 * Force unqiue track names for the track table specified
6736 * Note the panel is a required parameter to enable the update of the names displayed
6737 * Specify if on tracks or else on routes
6738 */
6739static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6740{
6741 // . Search list for an instance of repeated name
6742 // . get track of this name
6743 // . create new name
6744 // . rename track & update equiv. treeview iter
6745 // . repeat until all different
6746
6747 same_track_name_udata udata;
6748
6749 GList *track_names = NULL;
6750 udata.has_same_track_name = FALSE;
6751 udata.same_track_name = NULL;
6752
6753 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6754
6755 // No tracks
6756 if ( ! track_names )
6757 return;
6758
6759 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6760
6761 // Still no tracks...
6762 if ( ! dummy_list1 )
6763 return;
6764
6765 while ( udata.has_same_track_name ) {
6766
6767 // Find a track with the same name
6768 VikTrack *trk;
6769 if ( ontrack )
6770 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6771 else
6772 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6773
6774 if ( ! trk ) {
6775 // Broken :(
6776 g_critical("Houston, we've had a problem.");
6777 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6778 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6779 return;
6780 }
6781
6782 // Rename it
6783 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6784 vik_track_set_name ( trk, newname );
6785
6786 trku_udata udataU;
6787 udataU.trk = trk;
6788 udataU.uuid = NULL;
6789
6790 // Need want key of it for treeview update
6791 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6792
6793 if ( trkf && udataU.uuid ) {
6794
6795 GtkTreeIter *it;
6796 if ( ontrack )
6797 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6798 else
6799 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6800
6801 if ( it ) {
6802 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6803 if ( ontrack )
6804 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6805 else
6806 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6807 }
6808 }
6809
6810 // Start trying to find same names again...
6811 track_names = NULL;
6812 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6813 udata.has_same_track_name = FALSE;
6814 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6815
6816 // No tracks any more - give up searching
6817 if ( ! dummy_list2 )
6818 udata.has_same_track_name = FALSE;
6819 }
6820
6821 // Update
6822 vik_layers_panel_emit_update ( vlp );
6823}
6824
6825static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6826{
6827 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6828 GtkTreeIter *iter;
6829
6830 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6831 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6832 iter = &(vtl->tracks_iter);
6833 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6834 break;
6835 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6836 iter = &(vtl->routes_iter);
6837 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6838 break;
6839 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6840 iter = &(vtl->waypoints_iter);
6841 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6842 break;
6843 }
6844
6845 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6846}
6847
6848static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6849{
6850 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6851 GtkTreeIter *iter;
6852
6853 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6854 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6855 iter = &(vtl->tracks_iter);
6856 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6857 break;
6858 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6859 iter = &(vtl->routes_iter);
6860 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6861 break;
6862 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6863 iter = &(vtl->waypoints_iter);
6864 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6865 break;
6866 }
6867
6868 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6869}
6870
6871/**
6872 *
6873 */
6874static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6875{
6876 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6877 GList *all = NULL;
6878
6879 // Ensure list of track names offered is unique
6880 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6881 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6882 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6883 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6884 }
6885 else
6886 return;
6887 }
6888
6889 // Sort list alphabetically for better presentation
6890 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6891
6892 if ( ! all ) {
6893 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6894 return;
6895 }
6896
6897 // Get list of items to delete from the user
6898 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6899 all,
6900 TRUE,
6901 _("Delete Selection"),
6902 _("Select tracks to delete"));
6903 g_list_free(all);
6904
6905 // Delete requested tracks
6906 // since specificly requested, IMHO no need for extra confirmation
6907 if ( delete_list ) {
6908 GList *l;
6909 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6910 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6911 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6912 }
6913 g_list_free(delete_list);
6914 vik_layer_emit_update( VIK_LAYER(vtl) );
6915 }
6916}
6917
6918/**
6919 *
6920 */
6921static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6922{
6923 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6924 GList *all = NULL;
6925
6926 // Ensure list of track names offered is unique
6927 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6928 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6929 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6930 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6931 }
6932 else
6933 return;
6934 }
6935
6936 // Sort list alphabetically for better presentation
6937 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6938
6939 if ( ! all ) {
6940 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6941 return;
6942 }
6943
6944 // Get list of items to delete from the user
6945 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6946 all,
6947 TRUE,
6948 _("Delete Selection"),
6949 _("Select routes to delete") );
6950 g_list_free(all);
6951
6952 // Delete requested routes
6953 // since specificly requested, IMHO no need for extra confirmation
6954 if ( delete_list ) {
6955 GList *l;
6956 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6957 // This deletes first route it finds of that name (but uniqueness is enforced above)
6958 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6959 }
6960 g_list_free(delete_list);
6961 vik_layer_emit_update( VIK_LAYER(vtl) );
6962 }
6963}
6964
6965typedef struct {
6966 gboolean has_same_waypoint_name;
6967 const gchar *same_waypoint_name;
6968} same_waypoint_name_udata;
6969
6970static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6971{
6972 const gchar* namea = (const gchar*) aa;
6973 const gchar* nameb = (const gchar*) bb;
6974
6975 // the test
6976 gint result = strcmp ( namea, nameb );
6977
6978 if ( result == 0 ) {
6979 // Found two names the same
6980 same_waypoint_name_udata *user_data = udata;
6981 user_data->has_same_waypoint_name = TRUE;
6982 user_data->same_waypoint_name = namea;
6983 }
6984
6985 // Leave ordering the same
6986 return 0;
6987}
6988
6989/**
6990 * Find out if any waypoints have the same name in this layer
6991 */
6992gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6993{
6994 // Sort items by name, then compare if any next to each other are the same
6995
6996 GList *waypoint_names = NULL;
6997 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6998
6999 // No waypoints
7000 if ( ! waypoint_names )
7001 return FALSE;
7002
7003 same_waypoint_name_udata udata;
7004 udata.has_same_waypoint_name = FALSE;
7005
7006 // Use sort routine to traverse list comparing items
7007 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7008 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7009 // Still no waypoints...
7010 if ( ! dummy_list )
7011 return FALSE;
7012
7013 return udata.has_same_waypoint_name;
7014}
7015
7016/**
7017 * Force unqiue waypoint names for this layer
7018 * Note the panel is a required parameter to enable the update of the names displayed
7019 */
7020static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7021{
7022 // . Search list for an instance of repeated name
7023 // . get waypoint of this name
7024 // . create new name
7025 // . rename waypoint & update equiv. treeview iter
7026 // . repeat until all different
7027
7028 same_waypoint_name_udata udata;
7029
7030 GList *waypoint_names = NULL;
7031 udata.has_same_waypoint_name = FALSE;
7032 udata.same_waypoint_name = NULL;
7033
7034 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7035
7036 // No waypoints
7037 if ( ! waypoint_names )
7038 return;
7039
7040 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7041
7042 // Still no waypoints...
7043 if ( ! dummy_list1 )
7044 return;
7045
7046 while ( udata.has_same_waypoint_name ) {
7047
7048 // Find a waypoint with the same name
7049 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7050
7051 if ( ! waypoint ) {
7052 // Broken :(
7053 g_critical("Houston, we've had a problem.");
7054 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7055 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7056 return;
7057 }
7058
7059 // Rename it
7060 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7061
7062 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7063
7064 // Start trying to find same names again...
7065 waypoint_names = NULL;
7066 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7067 udata.has_same_waypoint_name = FALSE;
7068 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7069
7070 // No waypoints any more - give up searching
7071 if ( ! dummy_list2 )
7072 udata.has_same_waypoint_name = FALSE;
7073 }
7074
7075 // Update
7076 vik_layers_panel_emit_update ( vlp );
7077}
7078
7079/**
7080 *
7081 */
7082static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7083{
7084 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7085 GList *all = NULL;
7086
7087 // Ensure list of waypoint names offered is unique
7088 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7089 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7090 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7091 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7092 }
7093 else
7094 return;
7095 }
7096
7097 // Sort list alphabetically for better presentation
7098 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7099 if ( ! all ) {
7100 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7101 return;
7102 }
7103
7104 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7105
7106 // Get list of items to delete from the user
7107 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7108 all,
7109 TRUE,
7110 _("Delete Selection"),
7111 _("Select waypoints to delete"));
7112 g_list_free(all);
7113
7114 // Delete requested waypoints
7115 // since specificly requested, IMHO no need for extra confirmation
7116 if ( delete_list ) {
7117 GList *l;
7118 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7119 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7120 trw_layer_delete_waypoint_by_name (vtl, l->data);
7121 }
7122 g_list_free(delete_list);
7123
7124 trw_layer_calculate_bounds_waypoints ( vtl );
7125 vik_layer_emit_update( VIK_LAYER(vtl) );
7126 }
7127
7128}
7129
7130/**
7131 *
7132 */
7133static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7134{
7135 vik_treeview_item_toggle_visible ( vt, it );
7136}
7137
7138/**
7139 *
7140 */
7141static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7142{
7143 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7144}
7145
7146/**
7147 *
7148 */
7149static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7150{
7151 wp->visible = GPOINTER_TO_INT (on_off);
7152}
7153
7154/**
7155 *
7156 */
7157static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7158{
7159 wp->visible = !wp->visible;
7160}
7161
7162/**
7163 *
7164 */
7165static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7166{
7167 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7168 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7169 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7170 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7171 // Redraw
7172 vik_layer_emit_update ( VIK_LAYER(vtl) );
7173}
7174
7175/**
7176 *
7177 */
7178static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7179{
7180 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7181 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7182 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7183 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7184 // Redraw
7185 vik_layer_emit_update ( VIK_LAYER(vtl) );
7186}
7187
7188/**
7189 *
7190 */
7191static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7192{
7193 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7194 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7195 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7196 // Redraw
7197 vik_layer_emit_update ( VIK_LAYER(vtl) );
7198}
7199
7200/**
7201 *
7202 */
7203static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7204{
7205 trk->visible = GPOINTER_TO_INT (on_off);
7206}
7207
7208/**
7209 *
7210 */
7211static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7212{
7213 trk->visible = !trk->visible;
7214}
7215
7216/**
7217 *
7218 */
7219static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7220{
7221 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7222 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7223 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7224 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7225 // Redraw
7226 vik_layer_emit_update ( VIK_LAYER(vtl) );
7227}
7228
7229/**
7230 *
7231 */
7232static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7233{
7234 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7235 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7236 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7237 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7238 // Redraw
7239 vik_layer_emit_update ( VIK_LAYER(vtl) );
7240}
7241
7242/**
7243 *
7244 */
7245static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7246{
7247 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7248 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7249 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7250 // Redraw
7251 vik_layer_emit_update ( VIK_LAYER(vtl) );
7252}
7253
7254/**
7255 *
7256 */
7257static void trw_layer_routes_visibility_off ( menu_array_layer values )
7258{
7259 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7260 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7261 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7262 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7263 // Redraw
7264 vik_layer_emit_update ( VIK_LAYER(vtl) );
7265}
7266
7267/**
7268 *
7269 */
7270static void trw_layer_routes_visibility_on ( menu_array_layer values )
7271{
7272 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7273 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7274 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7275 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7276 // Redraw
7277 vik_layer_emit_update ( VIK_LAYER(vtl) );
7278}
7279
7280/**
7281 *
7282 */
7283static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7284{
7285 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7286 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7287 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7288 // Redraw
7289 vik_layer_emit_update ( VIK_LAYER(vtl) );
7290}
7291
7292/**
7293 * vik_trw_layer_build_waypoint_list_t:
7294 *
7295 * Helper function to construct a list of #vik_trw_waypoint_list_t
7296 */
7297GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7298{
7299 GList *waypoints_and_layers = NULL;
7300 // build waypoints_and_layers list
7301 while ( waypoints ) {
7302 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7303 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7304 vtdl->vtl = vtl;
7305 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7306 waypoints = g_list_next ( waypoints );
7307 }
7308 return waypoints_and_layers;
7309}
7310
7311/**
7312 * trw_layer_create_waypoint_list:
7313 *
7314 * Create the latest list of waypoints with the associated layer(s)
7315 * Although this will always be from a single layer here
7316 */
7317static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7318{
7319 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7320 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7321
7322 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7323}
7324
7325/**
7326 * trw_layer_analyse_close:
7327 *
7328 * Stuff to do on dialog closure
7329 */
7330static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7331{
7332 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7333 gtk_widget_destroy ( dialog );
7334 vtl->tracks_analysis_dialog = NULL;
7335}
7336
7337/**
7338 * vik_trw_layer_build_track_list_t:
7339 *
7340 * Helper function to construct a list of #vik_trw_track_list_t
7341 */
7342GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7343{
7344 GList *tracks_and_layers = NULL;
7345 // build tracks_and_layers list
7346 while ( tracks ) {
7347 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7348 vtdl->trk = VIK_TRACK(tracks->data);
7349 vtdl->vtl = vtl;
7350 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7351 tracks = g_list_next ( tracks );
7352 }
7353 return tracks_and_layers;
7354}
7355
7356/**
7357 * trw_layer_create_track_list:
7358 *
7359 * Create the latest list of tracks with the associated layer(s)
7360 * Although this will always be from a single layer here
7361 */
7362static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7363{
7364 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7365 GList *tracks = NULL;
7366 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7367 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7368 else
7369 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7370
7371 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7372}
7373
7374static void trw_layer_tracks_stats ( menu_array_layer values )
7375{
7376 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7377 // There can only be one!
7378 if ( vtl->tracks_analysis_dialog )
7379 return;
7380
7381 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7382 VIK_LAYER(vtl)->name,
7383 VIK_LAYER(vtl),
7384 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7385 trw_layer_create_track_list,
7386 trw_layer_analyse_close );
7387}
7388
7389/**
7390 *
7391 */
7392static void trw_layer_routes_stats ( menu_array_layer values )
7393{
7394 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7395 // There can only be one!
7396 if ( vtl->tracks_analysis_dialog )
7397 return;
7398
7399 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7400 VIK_LAYER(vtl)->name,
7401 VIK_LAYER(vtl),
7402 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7403 trw_layer_create_track_list,
7404 trw_layer_analyse_close );
7405}
7406
7407static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7408{
7409 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7410 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7411 if ( wp )
7412 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7413}
7414
7415static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7416{
7417 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7418 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7419 if ( !wp )
7420 return;
7421 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7422 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7423 g_free ( webpage );
7424}
7425
7426static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7427{
7428 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7429 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7430 if ( !wp )
7431 return;
7432 if ( wp->url ) {
7433 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7434 } else if ( !strncmp(wp->comment, "http", 4) ) {
7435 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7436 } else if ( !strncmp(wp->description, "http", 4) ) {
7437 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7438 }
7439}
7440
7441static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7442{
7443 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7444 {
7445 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7446
7447 // No actual change to the name supplied
7448 if ( wp->name )
7449 if (strcmp(newname, wp->name) == 0 )
7450 return NULL;
7451
7452 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7453
7454 if ( wpf ) {
7455 // An existing waypoint has been found with the requested name
7456 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7457 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7458 newname ) )
7459 return NULL;
7460 }
7461
7462 // Update WP name and refresh the treeview
7463 vik_waypoint_set_name (wp, newname);
7464
7465 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7466 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7467
7468 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7469
7470 return newname;
7471 }
7472
7473 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7474 {
7475 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7476
7477 // No actual change to the name supplied
7478 if ( trk->name )
7479 if (strcmp(newname, trk->name) == 0)
7480 return NULL;
7481
7482 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7483
7484 if ( trkf ) {
7485 // An existing track has been found with the requested name
7486 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7487 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7488 newname ) )
7489 return NULL;
7490 }
7491 // Update track name and refresh GUI parts
7492 vik_track_set_name (trk, newname);
7493
7494 // Update any subwindows that could be displaying this track which has changed name
7495 // Only one Track Edit Window
7496 if ( l->current_tp_track == trk && l->tpwin ) {
7497 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7498 }
7499 // Property Dialog of the track
7500 vik_trw_layer_propwin_update ( trk );
7501
7502 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7503 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7504
7505 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7506
7507 return newname;
7508 }
7509
7510 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7511 {
7512 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7513
7514 // No actual change to the name supplied
7515 if ( trk->name )
7516 if (strcmp(newname, trk->name) == 0)
7517 return NULL;
7518
7519 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7520
7521 if ( trkf ) {
7522 // An existing track has been found with the requested name
7523 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7524 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7525 newname ) )
7526 return NULL;
7527 }
7528 // Update track name and refresh GUI parts
7529 vik_track_set_name (trk, newname);
7530
7531 // Update any subwindows that could be displaying this track which has changed name
7532 // Only one Track Edit Window
7533 if ( l->current_tp_track == trk && l->tpwin ) {
7534 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7535 }
7536 // Property Dialog of the track
7537 vik_trw_layer_propwin_update ( trk );
7538
7539 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7540 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7541
7542 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7543
7544 return newname;
7545 }
7546 return NULL;
7547}
7548
7549static gboolean is_valid_geocache_name ( gchar *str )
7550{
7551 gint len = strlen ( str );
7552 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]));
7553}
7554
7555static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7556{
7557 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7558 a_acquire_set_filter_track ( trk );
7559}
7560
7561#ifdef VIK_CONFIG_GOOGLE
7562static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7563{
7564 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7565 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7566}
7567
7568static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7569{
7570 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7571 if ( tr ) {
7572 gchar *escaped = uri_escape ( tr->comment );
7573 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7574 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7575 g_free ( escaped );
7576 g_free ( webpage );
7577 }
7578}
7579#endif
7580
7581/* vlp can be NULL if necessary - i.e. right-click from a tool */
7582/* viewpoint is now available instead */
7583static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7584{
7585 static menu_array_sublayer pass_along;
7586 GtkWidget *item;
7587 gboolean rv = FALSE;
7588
7589 pass_along[MA_VTL] = l;
7590 pass_along[MA_VLP] = vlp;
7591 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7592 pass_along[MA_SUBLAYER_ID] = sublayer;
7593 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7594 pass_along[MA_VVP] = vvp;
7595 pass_along[MA_TV_ITER] = iter;
7596 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7597
7598 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7599 {
7600 rv = TRUE;
7601
7602 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7604 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7605 gtk_widget_show ( item );
7606
7607 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7608 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7609 if (tr && tr->property_dialog)
7610 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7611 }
7612 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7613 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7614 if (tr && tr->property_dialog)
7615 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7616 }
7617
7618 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7621 gtk_widget_show ( item );
7622
7623 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7625 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7626 gtk_widget_show ( item );
7627
7628 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7630 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7631 gtk_widget_show ( item );
7632
7633 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7634 {
7635 // Always create separator as now there is always at least the transform menu option
7636 item = gtk_menu_item_new ();
7637 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7638 gtk_widget_show ( item );
7639
7640 /* could be a right-click using the tool */
7641 if ( vlp != NULL ) {
7642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7645 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7646 gtk_widget_show ( item );
7647 }
7648
7649 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7650
7651 if ( wp && wp->name ) {
7652 if ( is_valid_geocache_name ( wp->name ) ) {
7653 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7656 gtk_widget_show ( item );
7657 }
7658#ifdef VIK_CONFIG_GEOTAG
7659 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7661 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7662 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7663 gtk_widget_show ( item );
7664#endif
7665 }
7666
7667 if ( wp && wp->image )
7668 {
7669 // Set up image paramater
7670 pass_along[MA_MISC] = wp->image;
7671
7672 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7673 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7674 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7675 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7676 gtk_widget_show ( item );
7677
7678#ifdef VIK_CONFIG_GEOTAG
7679 GtkWidget *geotag_submenu = gtk_menu_new ();
7680 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7681 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7682 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7683 gtk_widget_show ( item );
7684 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7685
7686 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7687 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7688 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7689 gtk_widget_show ( item );
7690
7691 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7693 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7694 gtk_widget_show ( item );
7695#endif
7696 }
7697
7698 if ( wp )
7699 {
7700 if ( wp->url ||
7701 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7702 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7703 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7704 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7705 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7706 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7707 gtk_widget_show ( item );
7708 }
7709 }
7710 }
7711 }
7712
7713 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7714 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7717 gtk_widget_show ( item );
7718 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7719 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7720 gtk_widget_set_sensitive ( item, TRUE );
7721 else
7722 gtk_widget_set_sensitive ( item, FALSE );
7723
7724 // Add separator
7725 item = gtk_menu_item_new ();
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7727 gtk_widget_show ( item );
7728 }
7729
7730 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7731 {
7732 rv = TRUE;
7733 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7734 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7736 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7737 gtk_widget_show ( item );
7738 }
7739
7740 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7741 {
7742 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7743 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7744 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7746 gtk_widget_show ( item );
7747
7748 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7749 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7750 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7751 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7752 gtk_widget_show ( item );
7753
7754 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7755 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7756 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7757 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7758 gtk_widget_show ( item );
7759
7760 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7761 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7763 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7764 gtk_widget_show ( item );
7765
7766 GtkWidget *vis_submenu = gtk_menu_new ();
7767 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7768 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7769 gtk_widget_show ( item );
7770 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7771
7772 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7773 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7775 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7776 gtk_widget_show ( item );
7777
7778 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7779 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7781 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7782 gtk_widget_show ( item );
7783
7784 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7785 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7787 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7788 gtk_widget_show ( item );
7789
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7793 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7794 }
7795
7796 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7797 {
7798 rv = TRUE;
7799
7800 if ( l->current_track && !l->current_track->is_route ) {
7801 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7803 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7804 gtk_widget_show ( item );
7805 // Add separator
7806 item = gtk_menu_item_new ();
7807 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7808 gtk_widget_show ( item );
7809 }
7810
7811 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7812 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7814 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7815 gtk_widget_show ( item );
7816
7817 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7818 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7819 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7820 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7821 gtk_widget_show ( item );
7822 // Make it available only when a new track *not* already in progress
7823 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7824
7825 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7826 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7828 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7829 gtk_widget_show ( item );
7830
7831 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7832 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7833 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7834 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7835 gtk_widget_show ( item );
7836
7837 GtkWidget *vis_submenu = gtk_menu_new ();
7838 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7839 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7840 gtk_widget_show ( item );
7841 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7842
7843 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7844 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7846 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7847 gtk_widget_show ( item );
7848
7849 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7850 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7852 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7853 gtk_widget_show ( item );
7854
7855 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7856 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7857 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7858 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7859
7860 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7863 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7864 gtk_widget_show ( item );
7865
7866 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7868 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7869 gtk_widget_show ( item );
7870 }
7871
7872 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7873 {
7874 rv = TRUE;
7875
7876 if ( l->current_track && l->current_track->is_route ) {
7877 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7878 // Reuse finish track method
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7880 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7881 gtk_widget_show ( item );
7882 // Add separator
7883 item = gtk_menu_item_new ();
7884 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7885 gtk_widget_show ( item );
7886 }
7887
7888 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7889 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7890 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7891 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7892 gtk_widget_show ( item );
7893
7894 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7895 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7897 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7898 gtk_widget_show ( item );
7899 // Make it available only when a new track *not* already in progress
7900 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7901
7902 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7903 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7905 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7906 gtk_widget_show ( item );
7907
7908 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7909 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7911 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7912 gtk_widget_show ( item );
7913
7914 GtkWidget *vis_submenu = gtk_menu_new ();
7915 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7916 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7917 gtk_widget_show ( item );
7918 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7919
7920 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7921 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7923 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7924 gtk_widget_show ( item );
7925
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7929 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7930 gtk_widget_show ( item );
7931
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7933 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7935 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7936
7937 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7940 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7941
7942 gtk_widget_show ( item );
7943
7944 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7947 gtk_widget_show ( item );
7948 }
7949
7950
7951 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7952 GtkWidget *submenu_sort = gtk_menu_new ();
7953 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7954 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7955 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7956 gtk_widget_show ( item );
7957 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7958
7959 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7961 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7962 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7963 gtk_widget_show ( item );
7964
7965 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7968 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7969 gtk_widget_show ( item );
7970 }
7971
7972 GtkWidget *upload_submenu = gtk_menu_new ();
7973
7974 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7975 {
7976 item = gtk_menu_item_new ();
7977 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7978 gtk_widget_show ( item );
7979
7980 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7981 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7982 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7983 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7984 if ( l->current_track ) {
7985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7986 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7987 gtk_widget_show ( item );
7988
7989 // Add separator
7990 item = gtk_menu_item_new ();
7991 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7992 gtk_widget_show ( item );
7993 }
7994
7995 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7996 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7997 else
7998 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7999 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8001 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8002 gtk_widget_show ( item );
8003
8004 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8006 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8007 gtk_widget_show ( item );
8008
8009 GtkWidget *goto_submenu;
8010 goto_submenu = gtk_menu_new ();
8011 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8012 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8013 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8014 gtk_widget_show ( item );
8015 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8016
8017 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8018 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8020 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8021 gtk_widget_show ( item );
8022
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8024 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8026 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8027 gtk_widget_show ( item );
8028
8029 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8030 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8032 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8033 gtk_widget_show ( item );
8034
8035 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8036 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8038 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8039 gtk_widget_show ( item );
8040
8041 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8042 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8044 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8045 gtk_widget_show ( item );
8046
8047 // Routes don't have speeds
8048 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8053 gtk_widget_show ( item );
8054 }
8055
8056 GtkWidget *combine_submenu;
8057 combine_submenu = gtk_menu_new ();
8058 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8059 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8060 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8061 gtk_widget_show ( item );
8062 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8063
8064 // Routes don't have times or segments...
8065 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8066 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8068 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8069 gtk_widget_show ( item );
8070
8071 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8073 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8074 gtk_widget_show ( item );
8075 }
8076
8077 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8079 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8080 gtk_widget_show ( item );
8081
8082 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8083 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8084 else
8085 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8087 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8088 gtk_widget_show ( item );
8089
8090 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8091 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8092 else
8093 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8095 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8096 gtk_widget_show ( item );
8097
8098 GtkWidget *split_submenu;
8099 split_submenu = gtk_menu_new ();
8100 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8101 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8102 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8103 gtk_widget_show ( item );
8104 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8105
8106 // Routes don't have times or segments...
8107 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8108 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8110 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8111 gtk_widget_show ( item );
8112
8113 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8114 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8116 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8117 gtk_widget_show ( item );
8118 }
8119
8120 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8122 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8123 gtk_widget_show ( item );
8124
8125 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8127 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8128 gtk_widget_show ( item );
8129 // Make it available only when a trackpoint is selected.
8130 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8131
8132 GtkWidget *insert_submenu = gtk_menu_new ();
8133 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8135 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8136 gtk_widget_show ( item );
8137 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8138
8139 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8141 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8142 gtk_widget_show ( item );
8143 // Make it available only when a point is selected
8144 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8145
8146 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8148 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8149 gtk_widget_show ( item );
8150 // Make it available only when a point is selected
8151 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8152
8153 GtkWidget *delete_submenu;
8154 delete_submenu = gtk_menu_new ();
8155 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8157 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8158 gtk_widget_show ( item );
8159 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8160
8161 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8162 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8164 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8165 gtk_widget_show ( item );
8166 // Make it available only when a point is selected
8167 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8168
8169 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8171 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8172 gtk_widget_show ( item );
8173
8174 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8176 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8177 gtk_widget_show ( item );
8178
8179 GtkWidget *transform_submenu;
8180 transform_submenu = gtk_menu_new ();
8181 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8182 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8183 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8184 gtk_widget_show ( item );
8185 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8186
8187 GtkWidget *dem_submenu;
8188 dem_submenu = gtk_menu_new ();
8189 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8190 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8191 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8192 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8193
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8196 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8197 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8198 gtk_widget_show ( item );
8199
8200 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8201 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8202 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8203 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8204 gtk_widget_show ( item );
8205
8206 GtkWidget *smooth_submenu;
8207 smooth_submenu = gtk_menu_new ();
8208 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8209 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8210 gtk_widget_show ( item );
8211 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8212
8213 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8215 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8216 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8217 gtk_widget_show ( item );
8218
8219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8221 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8222 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8223 gtk_widget_show ( item );
8224
8225 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8226 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8227 else
8228 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8231 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8232 gtk_widget_show ( item );
8233
8234 // Routes don't have timestamps - so this is only available for tracks
8235 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8236 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8237 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8238 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8239 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8240 gtk_widget_show ( item );
8241 }
8242
8243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8244 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8245 else
8246 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8247 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8249 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8250 gtk_widget_show ( item );
8251
8252 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8253 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8257 gtk_widget_show ( item );
8258 }
8259
8260 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8261 if ( vlp ) {
8262 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8263 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8264 else
8265 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8266 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8267 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8268 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8269 gtk_widget_show ( item );
8270 }
8271
8272 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8273 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8274 else
8275 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8278 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8279 gtk_widget_show ( item );
8280
8281 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8282 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8283 else
8284 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8287 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8288 gtk_widget_show ( item );
8289
8290 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8291 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8295 gtk_widget_show ( item );
8296 }
8297
8298 // ATM can't upload a single waypoint but can do waypoints to a GPS
8299 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8300 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8301 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8302 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8303 gtk_widget_show ( item );
8304 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8305
8306 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8307 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8309 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8310 gtk_widget_show ( item );
8311 }
8312 }
8313
8314 // Only made available if a suitable program is installed
8315 if ( have_diary_program ) {
8316 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8317 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8318 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8319 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8320 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8321 gtk_widget_show ( item );
8322 }
8323 }
8324
8325#ifdef VIK_CONFIG_GOOGLE
8326 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8327 {
8328 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8329 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8330 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8331 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8332 gtk_widget_show ( item );
8333 }
8334#endif
8335
8336 // Some things aren't usable with routes
8337 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8338#ifdef VIK_CONFIG_OPENSTREETMAP
8339 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8340 // Convert internal pointer into track
8341 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8342 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8344 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8345 gtk_widget_show ( item );
8346#endif
8347
8348 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8349 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8350 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8351 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8352 gtk_widget_show ( item );
8353
8354 /* ATM This function is only available via the layers panel, due to needing a vlp */
8355 if ( vlp ) {
8356 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8357 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8358 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8359 if ( item ) {
8360 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8361 gtk_widget_show ( item );
8362 }
8363 }
8364
8365#ifdef VIK_CONFIG_GEOTAG
8366 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8368 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8369 gtk_widget_show ( item );
8370#endif
8371 }
8372
8373 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8374 // Only show on viewport popmenu when a trackpoint is selected
8375 if ( ! vlp && l->current_tpl ) {
8376 // Add separator
8377 item = gtk_menu_item_new ();
8378 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8379 gtk_widget_show ( item );
8380
8381 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8382 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8384 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8385 gtk_widget_show ( item );
8386 }
8387 }
8388
8389 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8390 GtkWidget *transform_submenu;
8391 transform_submenu = gtk_menu_new ();
8392 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8394 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8395 gtk_widget_show ( item );
8396 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8397
8398 GtkWidget *dem_submenu;
8399 dem_submenu = gtk_menu_new ();
8400 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8401 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8402 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8403 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8404
8405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8407 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8408 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8409 gtk_widget_show ( item );
8410
8411 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8412 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8413 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8414 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8415 gtk_widget_show ( item );
8416 }
8417
8418 gtk_widget_show_all ( GTK_WIDGET(menu) );
8419
8420 return rv;
8421}
8422
8423// TODO: Probably better to rework this track manipulation in viktrack.c
8424static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8425{
8426 // sanity check
8427 if (!vtl->current_tpl)
8428 return;
8429
8430 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8431 VikTrackpoint *tp_other = NULL;
8432
8433 if ( before ) {
8434 if (!vtl->current_tpl->prev)
8435 return;
8436 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8437 } else {
8438 if (!vtl->current_tpl->next)
8439 return;
8440 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8441 }
8442
8443 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8444 if ( tp_other ) {
8445
8446 VikTrackpoint *tp_new = vik_trackpoint_new();
8447 struct LatLon ll_current, ll_other;
8448 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8449 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8450
8451 /* main positional interpolation */
8452 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8453 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8454
8455 /* Now other properties that can be interpolated */
8456 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8457
8458 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8459 /* Note here the division is applied to each part, then added
8460 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8461 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8462 tp_new->has_timestamp = TRUE;
8463 }
8464
8465 if (tp_current->speed != NAN && tp_other->speed != NAN)
8466 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8467
8468 /* TODO - improve interpolation of course, as it may not be correct.
8469 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8470 [similar applies if value is in radians] */
8471 if (tp_current->course != NAN && tp_other->course != NAN)
8472 tp_new->course = (tp_current->course + tp_other->course)/2;
8473
8474 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8475
8476 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8477 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8478 if ( !trk )
8479 // Otherwise try routes
8480 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8481 if ( !trk )
8482 return;
8483
8484 gint index = g_list_index ( trk->trackpoints, tp_current );
8485 if ( index > -1 ) {
8486 if ( !before )
8487 index = index + 1;
8488 // NB no recalculation of bounds since it is inserted between points
8489 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8490 }
8491 }
8492}
8493
8494static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8495{
8496 if ( vtl->tpwin )
8497 {
8498 if ( destroy)
8499 {
8500 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8501 vtl->tpwin = NULL;
8502 }
8503 else
8504 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8505 }
8506 if ( vtl->current_tpl )
8507 {
8508 vtl->current_tpl = NULL;
8509 vtl->current_tp_track = NULL;
8510 vtl->current_tp_id = NULL;
8511 vik_layer_emit_update(VIK_LAYER(vtl));
8512 }
8513}
8514
8515static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8516{
8517 VikTrack *trk = vtl->current_tp_track;
8518 VikCoord vc;
8519 // Notional center of a track is simply an average of the bounding box extremities
8520 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8521 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, &center );
8522 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8523}
8524
8525static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8526{
8527 g_assert ( vtl->tpwin != NULL );
8528 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8529 trw_layer_cancel_current_tp ( vtl, TRUE );
8530
8531 if ( vtl->current_tpl == NULL )
8532 return;
8533
8534 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8535 {
8536 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8537 my_tpwin_set_tp ( vtl );
8538 }
8539 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8540 {
8541 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8542 if ( tr == NULL )
8543 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8544 if ( tr == NULL )
8545 return;
8546
8547 trw_layer_trackpoint_selected_delete ( vtl, tr );
8548
8549 if ( vtl->current_tpl )
8550 // Reset dialog with the available adjacent trackpoint
8551 my_tpwin_set_tp ( vtl );
8552
8553 vik_layer_emit_update(VIK_LAYER(vtl));
8554 }
8555 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8556 {
8557 if ( vtl->current_tp_track ) {
8558 vtl->current_tpl = vtl->current_tpl->next;
8559 my_tpwin_set_tp ( vtl );
8560 }
8561 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8562 }
8563 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8564 {
8565 if ( vtl->current_tp_track ) {
8566 vtl->current_tpl = vtl->current_tpl->prev;
8567 my_tpwin_set_tp ( vtl );
8568 }
8569 vik_layer_emit_update(VIK_LAYER(vtl));
8570 }
8571 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8572 {
8573 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8574 vik_layer_emit_update(VIK_LAYER(vtl));
8575 }
8576 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8577 vik_layer_emit_update(VIK_LAYER(vtl));
8578}
8579
8580/**
8581 * trw_layer_dialog_shift:
8582 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8583 *
8584 * Try to reposition a dialog if it's over the specified coord
8585 * so to not obscure the item of interest
8586 */
8587void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8588{
8589 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8590
8591 // Attempt force dialog to be shown so we can find out where it is more reliably...
8592 while ( gtk_events_pending() )
8593 gtk_main_iteration ();
8594
8595 // get parent window position & size
8596 gint win_pos_x, win_pos_y;
8597 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8598
8599 gint win_size_x, win_size_y;
8600 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8601
8602 // get own dialog size
8603 gint dia_size_x, dia_size_y;
8604 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8605
8606 // get own dialog position
8607 gint dia_pos_x, dia_pos_y;
8608 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8609
8610 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8611 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8612
8613 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8614
8615 gint vp_xx, vp_yy; // In viewport pixels
8616 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8617
8618 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8619
8620 gint dest_x = 0;
8621 gint dest_y = 0;
8622 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8623
8624 // Transform Viewport pixels into absolute pixels
8625 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8626 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8627
8628 // Is dialog over the point (to within an ^^ edge value)
8629 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8630 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8631
8632 if ( vertical ) {
8633 // Shift up<->down
8634 gint hh = vik_viewport_get_height ( vvp );
8635
8636 // Consider the difference in viewport to the full window
8637 gint offset_y = dest_y;
8638 // Add difference between dialog and window sizes
8639 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8640
8641 if ( vp_yy > hh/2 ) {
8642 // Point in bottom half, move window to top half
8643 gtk_window_move ( dialog, dia_pos_x, offset_y );
8644 }
8645 else {
8646 // Point in top half, move dialog down
8647 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8648 }
8649 }
8650 else {
8651 // Shift left<->right
8652 gint ww = vik_viewport_get_width ( vvp );
8653
8654 // Consider the difference in viewport to the full window
8655 gint offset_x = dest_x;
8656 // Add difference between dialog and window sizes
8657 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8658
8659 if ( vp_xx > ww/2 ) {
8660 // Point on right, move window to left
8661 gtk_window_move ( dialog, offset_x, dia_pos_y );
8662 }
8663 else {
8664 // Point on left, move right
8665 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8666 }
8667 }
8668 }
8669 }
8670 }
8671}
8672
8673static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8674{
8675 if ( ! vtl->tpwin )
8676 {
8677 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8678 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8679 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8680 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8681
8682 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8683
8684 if ( vtl->current_tpl ) {
8685 // get tp pixel position
8686 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8687
8688 // Shift up<->down to try not to obscure the trackpoint.
8689 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8690 }
8691 }
8692
8693 if ( vtl->current_tpl )
8694 if ( vtl->current_tp_track )
8695 my_tpwin_set_tp ( vtl );
8696 /* set layer name and TP data */
8697}
8698
8699/***************************************************************************
8700 ** Tool code
8701 ***************************************************************************/
8702
8703/*** Utility data structures and functions ****/
8704
8705typedef struct {
8706 gint x, y;
8707 gint closest_x, closest_y;
8708 gboolean draw_images;
8709 gpointer *closest_wp_id;
8710 VikWaypoint *closest_wp;
8711 VikViewport *vvp;
8712} WPSearchParams;
8713
8714typedef struct {
8715 gint x, y;
8716 gint closest_x, closest_y;
8717 gpointer closest_track_id;
8718 VikTrackpoint *closest_tp;
8719 VikViewport *vvp;
8720 GList *closest_tpl;
8721 LatLonBBox bbox;
8722} TPSearchParams;
8723
8724static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8725{
8726 gint x, y;
8727 if ( !wp->visible )
8728 return;
8729
8730 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8731
8732 // If waypoint has an image then use the image size to select
8733 if ( params->draw_images && wp->image ) {
8734 gint slackx, slacky;
8735 slackx = wp->image_width / 2;
8736 slacky = wp->image_height / 2;
8737
8738 if ( x <= params->x + slackx && x >= params->x - slackx
8739 && y <= params->y + slacky && y >= params->y - slacky ) {
8740 params->closest_wp_id = id;
8741 params->closest_wp = wp;
8742 params->closest_x = x;
8743 params->closest_y = y;
8744 }
8745 }
8746 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8747 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8748 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8749 {
8750 params->closest_wp_id = id;
8751 params->closest_wp = wp;
8752 params->closest_x = x;
8753 params->closest_y = y;
8754 }
8755}
8756
8757static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8758{
8759 GList *tpl = t->trackpoints;
8760 VikTrackpoint *tp;
8761
8762 if ( !t->visible )
8763 return;
8764
8765 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8766 return;
8767
8768 while (tpl)
8769 {
8770 gint x, y;
8771 tp = VIK_TRACKPOINT(tpl->data);
8772
8773 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8774
8775 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8776 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8777 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8778 {
8779 params->closest_track_id = id;
8780 params->closest_tp = tp;
8781 params->closest_tpl = tpl;
8782 params->closest_x = x;
8783 params->closest_y = y;
8784 }
8785 tpl = tpl->next;
8786 }
8787}
8788
8789// ATM: Leave this as 'Track' only.
8790// Not overly bothered about having a snap to route trackpoint capability
8791static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8792{
8793 TPSearchParams params;
8794 params.x = x;
8795 params.y = y;
8796 params.vvp = vvp;
8797 params.closest_track_id = NULL;
8798 params.closest_tp = NULL;
8799 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8800 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8801 return params.closest_tp;
8802}
8803
8804static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8805{
8806 WPSearchParams params;
8807 params.x = x;
8808 params.y = y;
8809 params.vvp = vvp;
8810 params.draw_images = vtl->drawimages;
8811 params.closest_wp = NULL;
8812 params.closest_wp_id = NULL;
8813 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8814 return params.closest_wp;
8815}
8816
8817
8818// Some forward declarations
8819static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8820static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8821static void marker_end_move ( tool_ed_t *t );
8822//
8823
8824static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
8825{
8826 if ( t->holding ) {
8827 VikCoord new_coord;
8828 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8829
8830 // Here always allow snapping back to the original location
8831 // this is useful when one decides not to move the thing afterall
8832 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8833
8834 // snap to TP
8835 if ( event->state & GDK_CONTROL_MASK )
8836 {
8837 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8838 if ( tp )
8839 new_coord = tp->coord;
8840 }
8841
8842 // snap to WP
8843 if ( event->state & GDK_SHIFT_MASK )
8844 {
8845 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8846 if ( wp )
8847 new_coord = wp->coord;
8848 }
8849
8850 gint x, y;
8851 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8852
8853 marker_moveto ( t, x, y );
8854
8855 return TRUE;
8856 }
8857 return FALSE;
8858}
8859
8860static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8861{
8862 if ( t->holding && event->button == 1 )
8863 {
8864 // Prevent accidental (small) shifts when specific movement has not been requested
8865 // (as the click release has occurred within the click object detection area)
8866 if ( !t->moving )
8867 return FALSE;
8868
8869 VikCoord new_coord;
8870 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8871
8872 // snap to TP
8873 if ( event->state & GDK_CONTROL_MASK )
8874 {
8875 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8876 if ( tp )
8877 new_coord = tp->coord;
8878 }
8879
8880 // snap to WP
8881 if ( event->state & GDK_SHIFT_MASK )
8882 {
8883 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8884 if ( wp )
8885 new_coord = wp->coord;
8886 }
8887
8888 marker_end_move ( t );
8889
8890 // Determine if working on a waypoint or a trackpoint
8891 if ( t->is_waypoint ) {
8892 // Update waypoint position
8893 vtl->current_wp->coord = new_coord;
8894 trw_layer_calculate_bounds_waypoints ( vtl );
8895 // Reset waypoint pointer
8896 vtl->current_wp = NULL;
8897 vtl->current_wp_id = NULL;
8898 }
8899 else {
8900 if ( vtl->current_tpl ) {
8901 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8902
8903 if ( vtl->current_tp_track )
8904 vik_track_calculate_bounds ( vtl->current_tp_track );
8905
8906 if ( vtl->tpwin )
8907 if ( vtl->current_tp_track )
8908 my_tpwin_set_tp ( vtl );
8909 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8910 }
8911 }
8912
8913 vik_layer_emit_update ( VIK_LAYER(vtl) );
8914 return TRUE;
8915 }
8916 return FALSE;
8917}
8918
8919/*
8920 Returns true if a waypoint or track is found near the requested event position for this particular layer
8921 The item found is automatically selected
8922 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8923 */
8924static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8925{
8926 if ( event->button != 1 )
8927 return FALSE;
8928
8929 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8930 return FALSE;
8931
8932 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8933 return FALSE;
8934
8935 LatLonBBox bbox;
8936 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8937
8938 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8939
8940 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8941 WPSearchParams wp_params;
8942 wp_params.vvp = vvp;
8943 wp_params.x = event->x;
8944 wp_params.y = event->y;
8945 wp_params.draw_images = vtl->drawimages;
8946 wp_params.closest_wp_id = NULL;
8947 wp_params.closest_wp = NULL;
8948
8949 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8950
8951 if ( wp_params.closest_wp ) {
8952
8953 // Select
8954 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8955
8956 // Too easy to move it so must be holding shift to start immediately moving it
8957 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8958 if ( event->state & GDK_SHIFT_MASK ||
8959 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8960 // Put into 'move buffer'
8961 // NB vvp & vw already set in tet
8962 tet->vtl = (gpointer)vtl;
8963 tet->is_waypoint = TRUE;
8964
8965 marker_begin_move (tet, event->x, event->y);
8966 }
8967
8968 vtl->current_wp = wp_params.closest_wp;
8969 vtl->current_wp_id = wp_params.closest_wp_id;
8970
8971 if ( event->type == GDK_2BUTTON_PRESS ) {
8972 if ( vtl->current_wp->image ) {
8973 menu_array_sublayer values;
8974 values[MA_VTL] = vtl;
8975 values[MA_MISC] = vtl->current_wp->image;
8976 trw_layer_show_picture ( values );
8977 }
8978 }
8979
8980 vik_layer_emit_update ( VIK_LAYER(vtl) );
8981
8982 return TRUE;
8983 }
8984 }
8985
8986 // Used for both track and route lists
8987 TPSearchParams tp_params;
8988 tp_params.vvp = vvp;
8989 tp_params.x = event->x;
8990 tp_params.y = event->y;
8991 tp_params.closest_track_id = NULL;
8992 tp_params.closest_tp = NULL;
8993 tp_params.closest_tpl = NULL;
8994 tp_params.bbox = bbox;
8995
8996 if (vtl->tracks_visible) {
8997 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8998
8999 if ( tp_params.closest_tp ) {
9000
9001 // Always select + highlight the track
9002 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9003
9004 tet->is_waypoint = FALSE;
9005
9006 // Select the Trackpoint
9007 // Can move it immediately when control held or it's the previously selected tp
9008 if ( event->state & GDK_CONTROL_MASK ||
9009 vtl->current_tpl == tp_params.closest_tpl ) {
9010 // Put into 'move buffer'
9011 // NB vvp & vw already set in tet
9012 tet->vtl = (gpointer)vtl;
9013 marker_begin_move (tet, event->x, event->y);
9014 }
9015
9016 vtl->current_tpl = tp_params.closest_tpl;
9017 vtl->current_tp_id = tp_params.closest_track_id;
9018 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9019
9020 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9021
9022 if ( vtl->tpwin )
9023 my_tpwin_set_tp ( vtl );
9024
9025 vik_layer_emit_update ( VIK_LAYER(vtl) );
9026 return TRUE;
9027 }
9028 }
9029
9030 // Try again for routes
9031 if (vtl->routes_visible) {
9032 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9033
9034 if ( tp_params.closest_tp ) {
9035
9036 // Always select + highlight the track
9037 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9038
9039 tet->is_waypoint = FALSE;
9040
9041 // Select the Trackpoint
9042 // Can move it immediately when control held or it's the previously selected tp
9043 if ( event->state & GDK_CONTROL_MASK ||
9044 vtl->current_tpl == tp_params.closest_tpl ) {
9045 // Put into 'move buffer'
9046 // NB vvp & vw already set in tet
9047 tet->vtl = (gpointer)vtl;
9048 marker_begin_move (tet, event->x, event->y);
9049 }
9050
9051 vtl->current_tpl = tp_params.closest_tpl;
9052 vtl->current_tp_id = tp_params.closest_track_id;
9053 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9054
9055 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9056
9057 if ( vtl->tpwin )
9058 my_tpwin_set_tp ( vtl );
9059
9060 vik_layer_emit_update ( VIK_LAYER(vtl) );
9061 return TRUE;
9062 }
9063 }
9064
9065 /* these aren't the droids you're looking for */
9066 vtl->current_wp = NULL;
9067 vtl->current_wp_id = NULL;
9068 trw_layer_cancel_current_tp ( vtl, FALSE );
9069
9070 // Blank info
9071 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9072
9073 return FALSE;
9074}
9075
9076static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9077{
9078 if ( event->button != 3 )
9079 return FALSE;
9080
9081 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9082 return FALSE;
9083
9084 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9085 return FALSE;
9086
9087 /* Post menu for the currently selected item */
9088
9089 /* See if a track is selected */
9090 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9091 if ( track && track->visible ) {
9092
9093 if ( track->name ) {
9094
9095 if ( vtl->track_right_click_menu )
9096 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9097
9098 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9099
9100 trku_udata udataU;
9101 udataU.trk = track;
9102 udataU.uuid = NULL;
9103
9104 gpointer *trkf;
9105 if ( track->is_route )
9106 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9107 else
9108 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9109
9110 if ( trkf && udataU.uuid ) {
9111
9112 GtkTreeIter *iter;
9113 if ( track->is_route )
9114 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9115 else
9116 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9117
9118 trw_layer_sublayer_add_menu_items ( vtl,
9119 vtl->track_right_click_menu,
9120 NULL,
9121 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9122 udataU.uuid,
9123 iter,
9124 vvp );
9125 }
9126
9127 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9128
9129 return TRUE;
9130 }
9131 }
9132
9133 /* See if a waypoint is selected */
9134 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9135 if ( waypoint && waypoint->visible ) {
9136 if ( waypoint->name ) {
9137
9138 if ( vtl->wp_right_click_menu )
9139 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9140
9141 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9142
9143 wpu_udata udata;
9144 udata.wp = waypoint;
9145 udata.uuid = NULL;
9146
9147 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9148
9149 if ( wpf && udata.uuid ) {
9150 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9151
9152 trw_layer_sublayer_add_menu_items ( vtl,
9153 vtl->wp_right_click_menu,
9154 NULL,
9155 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9156 udata.uuid,
9157 iter,
9158 vvp );
9159 }
9160 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9161
9162 return TRUE;
9163 }
9164 }
9165
9166 return FALSE;
9167}
9168
9169/* background drawing hook, to be passed the viewport */
9170static gboolean tool_sync_done = TRUE;
9171
9172static gboolean tool_sync(gpointer data)
9173{
9174 VikViewport *vvp = data;
9175 gdk_threads_enter();
9176 vik_viewport_sync(vvp);
9177 tool_sync_done = TRUE;
9178 gdk_threads_leave();
9179 return FALSE;
9180}
9181
9182static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9183{
9184 t->holding = TRUE;
9185 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9186 gdk_gc_set_function ( t->gc, GDK_INVERT );
9187 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9188 vik_viewport_sync(t->vvp);
9189 t->oldx = x;
9190 t->oldy = y;
9191 t->moving = FALSE;
9192}
9193
9194static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9195{
9196 VikViewport *vvp = t->vvp;
9197 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9198 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9199 t->oldx = x;
9200 t->oldy = y;
9201 t->moving = TRUE;
9202
9203 if (tool_sync_done) {
9204 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9205 tool_sync_done = FALSE;
9206 }
9207}
9208
9209static void marker_end_move ( tool_ed_t *t )
9210{
9211 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9212 g_object_unref ( t->gc );
9213 t->holding = FALSE;
9214 t->moving = FALSE;
9215}
9216
9217/*** Edit waypoint ****/
9218
9219static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9220{
9221 tool_ed_t *t = g_new(tool_ed_t, 1);
9222 t->vvp = vvp;
9223 t->holding = FALSE;
9224 return t;
9225}
9226
9227static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9228{
9229 g_free ( t );
9230}
9231
9232static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9233{
9234 WPSearchParams params;
9235 tool_ed_t *t = data;
9236 VikViewport *vvp = t->vvp;
9237
9238 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9239 return FALSE;
9240
9241 if ( t->holding ) {
9242 return TRUE;
9243 }
9244
9245 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9246 return FALSE;
9247
9248 if ( vtl->current_wp && vtl->current_wp->visible )
9249 {
9250 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9251 gint x, y;
9252 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9253
9254 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9255 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9256 {
9257 if ( event->button == 3 )
9258 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9259 else {
9260 marker_begin_move(t, event->x, event->y);
9261 }
9262 return TRUE;
9263 }
9264 }
9265
9266 params.vvp = vvp;
9267 params.x = event->x;
9268 params.y = event->y;
9269 params.draw_images = vtl->drawimages;
9270 params.closest_wp_id = NULL;
9271 params.closest_wp = NULL;
9272 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
9273 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9274 {
9275 if ( event->button == 3 )
9276 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9277 else
9278 marker_begin_move(t, event->x, event->y);
9279 return FALSE;
9280 }
9281 else if ( params.closest_wp )
9282 {
9283 if ( event->button == 3 )
9284 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9285 else
9286 vtl->waypoint_rightclick = FALSE;
9287
9288 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9289
9290 vtl->current_wp = params.closest_wp;
9291 vtl->current_wp_id = params.closest_wp_id;
9292
9293 /* could make it so don't update if old WP is off screen and new is null but oh well */
9294 vik_layer_emit_update ( VIK_LAYER(vtl) );
9295 return TRUE;
9296 }
9297
9298 vtl->current_wp = NULL;
9299 vtl->current_wp_id = NULL;
9300 vtl->waypoint_rightclick = FALSE;
9301 vik_layer_emit_update ( VIK_LAYER(vtl) );
9302 return FALSE;
9303}
9304
9305static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9306{
9307 tool_ed_t *t = data;
9308 VikViewport *vvp = t->vvp;
9309
9310 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9311 return FALSE;
9312
9313 if ( t->holding ) {
9314 VikCoord new_coord;
9315 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9316
9317 /* snap to TP */
9318 if ( event->state & GDK_CONTROL_MASK )
9319 {
9320 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9321 if ( tp )
9322 new_coord = tp->coord;
9323 }
9324
9325 /* snap to WP */
9326 if ( event->state & GDK_SHIFT_MASK )
9327 {
9328 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9329 if ( wp && wp != vtl->current_wp )
9330 new_coord = wp->coord;
9331 }
9332
9333 {
9334 gint x, y;
9335 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9336
9337 marker_moveto ( t, x, y );
9338 }
9339 return TRUE;
9340 }
9341 return FALSE;
9342}
9343
9344static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9345{
9346 tool_ed_t *t = data;
9347 VikViewport *vvp = t->vvp;
9348
9349 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9350 return FALSE;
9351
9352 if ( t->holding && event->button == 1 )
9353 {
9354 VikCoord new_coord;
9355 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9356
9357 /* snap to TP */
9358 if ( event->state & GDK_CONTROL_MASK )
9359 {
9360 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9361 if ( tp )
9362 new_coord = tp->coord;
9363 }
9364
9365 /* snap to WP */
9366 if ( event->state & GDK_SHIFT_MASK )
9367 {
9368 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9369 if ( wp && wp != vtl->current_wp )
9370 new_coord = wp->coord;
9371 }
9372
9373 marker_end_move ( t );
9374
9375 vtl->current_wp->coord = new_coord;
9376
9377 trw_layer_calculate_bounds_waypoints ( vtl );
9378 vik_layer_emit_update ( VIK_LAYER(vtl) );
9379 return TRUE;
9380 }
9381 /* PUT IN RIGHT PLACE!!! */
9382 if ( event->button == 3 && vtl->waypoint_rightclick )
9383 {
9384 if ( vtl->wp_right_click_menu )
9385 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9386 if ( vtl->current_wp ) {
9387 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9388 trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
9389 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9390 }
9391 vtl->waypoint_rightclick = FALSE;
9392 }
9393 return FALSE;
9394}
9395
9396/*** New track ****/
9397
9398static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9399{
9400 return vvp;
9401}
9402
9403typedef struct {
9404 VikTrwLayer *vtl;
9405 GdkDrawable *drawable;
9406 GdkGC *gc;
9407 GdkPixmap *pixmap;
9408} draw_sync_t;
9409
9410/*
9411 * Draw specified pixmap
9412 */
9413static gboolean draw_sync ( gpointer data )
9414{
9415 draw_sync_t *ds = (draw_sync_t*) data;
9416 // Sometimes don't want to draw
9417 // normally because another update has taken precedent such as panning the display
9418 // which means this pixmap is no longer valid
9419 if ( ds->vtl->draw_sync_do ) {
9420 gdk_threads_enter();
9421 gdk_draw_drawable (ds->drawable,
9422 ds->gc,
9423 ds->pixmap,
9424 0, 0, 0, 0, -1, -1);
9425 ds->vtl->draw_sync_done = TRUE;
9426 gdk_threads_leave();
9427 }
9428 g_free ( ds );
9429 return FALSE;
9430}
9431
9432static gchar* distance_string (gdouble distance)
9433{
9434 gchar str[128];
9435
9436 /* draw label with distance */
9437 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9438 switch (dist_units) {
9439 case VIK_UNITS_DISTANCE_MILES:
9440 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9441 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9442 } else if (distance < 1609.4) {
9443 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9444 } else {
9445 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9446 }
9447 break;
9448 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9449 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9450 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9451 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9452 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9453 } else {
9454 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9455 }
9456 break;
9457 default:
9458 // VIK_UNITS_DISTANCE_KILOMETRES
9459 if (distance >= 1000 && distance < 100000) {
9460 g_sprintf(str, "%3.2f km", distance/1000.0);
9461 } else if (distance < 1000) {
9462 g_sprintf(str, "%d m", (int)distance);
9463 } else {
9464 g_sprintf(str, "%d km", (int)distance/1000);
9465 }
9466 break;
9467 }
9468 return g_strdup (str);
9469}
9470
9471/*
9472 * Actually set the message in statusbar
9473 */
9474static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9475{
9476 // Only show elevation data when track has some elevation properties
9477 gchar str_gain_loss[64];
9478 str_gain_loss[0] = '\0';
9479 gchar str_last_step[64];
9480 str_last_step[0] = '\0';
9481 gchar *str_total = distance_string (distance);
9482
9483 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9484 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9485 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9486 else
9487 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9488 }
9489
9490 if ( last_step > 0 ) {
9491 gchar *tmp = distance_string (last_step);
9492 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9493 g_free ( tmp );
9494 }
9495
9496 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9497
9498 // Write with full gain/loss information
9499 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9500 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9501 g_free ( msg );
9502 g_free ( str_total );
9503}
9504
9505/*
9506 * Figure out what information should be set in the statusbar and then write it
9507 */
9508static void update_statusbar ( VikTrwLayer *vtl )
9509{
9510 // Get elevation data
9511 gdouble elev_gain, elev_loss;
9512 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9513
9514 /* Find out actual distance of current track */
9515 gdouble distance = vik_track_get_length (vtl->current_track);
9516
9517 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9518}
9519
9520
9521static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9522{
9523 /* if we haven't sync'ed yet, we don't have time to do more. */
9524 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9525 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9526
9527 static GdkPixmap *pixmap = NULL;
9528 int w1, h1, w2, h2;
9529 // Need to check in case window has been resized
9530 w1 = vik_viewport_get_width(vvp);
9531 h1 = vik_viewport_get_height(vvp);
9532 if (!pixmap) {
9533 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9534 }
9535 gdk_drawable_get_size (pixmap, &w2, &h2);
9536 if (w1 != w2 || h1 != h2) {
9537 g_object_unref ( G_OBJECT ( pixmap ) );
9538 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9539 }
9540
9541 // Reset to background
9542 gdk_draw_drawable (pixmap,
9543 vtl->current_track_newpoint_gc,
9544 vik_viewport_get_pixmap(vvp),
9545 0, 0, 0, 0, -1, -1);
9546
9547 draw_sync_t *passalong;
9548 gint x1, y1;
9549
9550 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9551
9552 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9553 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9554 // thus when we come to reset to the background it would include what we have already drawn!!
9555 gdk_draw_line ( pixmap,
9556 vtl->current_track_newpoint_gc,
9557 x1, y1, event->x, event->y );
9558 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9559
9560 /* Find out actual distance of current track */
9561 gdouble distance = vik_track_get_length (vtl->current_track);
9562
9563 // Now add distance to where the pointer is //
9564 VikCoord coord;
9565 struct LatLon ll;
9566 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9567 vik_coord_to_latlon ( &coord, &ll );
9568 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9569 distance = distance + last_step;
9570
9571 // Get elevation data
9572 gdouble elev_gain, elev_loss;
9573 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9574
9575 // Adjust elevation data (if available) for the current pointer position
9576 gdouble elev_new;
9577 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9578 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9579 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9580 // Adjust elevation of last track point
9581 if ( elev_new > last_tpt->altitude )
9582 // Going up
9583 elev_gain += elev_new - last_tpt->altitude;
9584 else
9585 // Going down
9586 elev_loss += last_tpt->altitude - elev_new;
9587 }
9588 }
9589
9590 //
9591 // Display of the distance 'tooltip' during track creation is controlled by a preference
9592 //
9593 if ( a_vik_get_create_track_tooltip() ) {
9594
9595 gchar *str = distance_string (distance);
9596
9597 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9598 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9599 pango_layout_set_text (pl, str, -1);
9600 gint wd, hd;
9601 pango_layout_get_pixel_size ( pl, &wd, &hd );
9602
9603 gint xd,yd;
9604 // offset from cursor a bit depending on font size
9605 xd = event->x + 10;
9606 yd = event->y - hd;
9607
9608 // Create a background block to make the text easier to read over the background map
9609 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9610 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9611 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9612
9613 g_object_unref ( G_OBJECT ( pl ) );
9614 g_object_unref ( G_OBJECT ( background_block_gc ) );
9615 g_free (str);
9616 }
9617
9618 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9619 passalong->vtl = vtl;
9620 passalong->pixmap = pixmap;
9621 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9622 passalong->gc = vtl->current_track_newpoint_gc;
9623
9624 gdouble angle;
9625 gdouble baseangle;
9626 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9627
9628 // Update statusbar with full gain/loss information
9629 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9630
9631 // draw pixmap when we have time to
9632 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9633 vtl->draw_sync_done = FALSE;
9634 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9635 }
9636 return VIK_LAYER_TOOL_ACK;
9637}
9638
9639// NB vtl->current_track must be valid
9640static void undo_trackpoint_add ( VikTrwLayer *vtl )
9641{
9642 // 'undo'
9643 if ( vtl->current_track->trackpoints ) {
9644 // TODO rework this...
9645 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9646 GList *last = g_list_last(vtl->current_track->trackpoints);
9647 g_free ( last->data );
9648 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9649
9650 vik_track_calculate_bounds ( vtl->current_track );
9651 }
9652}
9653
9654static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9655{
9656 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9657 // Bin track if only one point as it's not very useful
9658 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9659 if ( vtl->current_track->is_route )
9660 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9661 else
9662 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9663 }
9664 vtl->current_track = NULL;
9665 vik_layer_emit_update ( VIK_LAYER(vtl) );
9666 return TRUE;
9667 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9668 undo_trackpoint_add ( vtl );
9669 update_statusbar ( vtl );
9670 vik_layer_emit_update ( VIK_LAYER(vtl) );
9671 return TRUE;
9672 }
9673 return FALSE;
9674}
9675
9676/*
9677 * Common function to handle trackpoint button requests on either a route or a track
9678 * . enables adding a point via normal click
9679 * . enables removal of last point via right click
9680 * . finishing of the track or route via double clicking
9681 */
9682static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9683{
9684 VikTrackpoint *tp;
9685
9686 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9687 return FALSE;
9688
9689 if ( event->button == 2 ) {
9690 // As the display is panning, the new track pixmap is now invalid so don't draw it
9691 // otherwise this drawing done results in flickering back to an old image
9692 vtl->draw_sync_do = FALSE;
9693 return FALSE;
9694 }
9695
9696 if ( event->button == 3 )
9697 {
9698 if ( !vtl->current_track )
9699 return FALSE;
9700 undo_trackpoint_add ( vtl );
9701 update_statusbar ( vtl );
9702 vik_layer_emit_update ( VIK_LAYER(vtl) );
9703 return TRUE;
9704 }
9705
9706 if ( event->type == GDK_2BUTTON_PRESS )
9707 {
9708 /* subtract last (duplicate from double click) tp then end */
9709 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9710 {
9711 /* undo last, then end */
9712 undo_trackpoint_add ( vtl );
9713 vtl->current_track = NULL;
9714 }
9715 vik_layer_emit_update ( VIK_LAYER(vtl) );
9716 return TRUE;
9717 }
9718
9719 tp = vik_trackpoint_new();
9720 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9721
9722 /* snap to other TP */
9723 if ( event->state & GDK_CONTROL_MASK )
9724 {
9725 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9726 if ( other_tp )
9727 tp->coord = other_tp->coord;
9728 }
9729
9730 tp->newsegment = FALSE;
9731 tp->has_timestamp = FALSE;
9732 tp->timestamp = 0;
9733
9734 if ( vtl->current_track ) {
9735 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9736 /* Auto attempt to get elevation from DEM data (if it's available) */
9737 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9738 }
9739
9740 vtl->ct_x1 = vtl->ct_x2;
9741 vtl->ct_y1 = vtl->ct_y2;
9742 vtl->ct_x2 = event->x;
9743 vtl->ct_y2 = event->y;
9744
9745 vik_layer_emit_update ( VIK_LAYER(vtl) );
9746 return TRUE;
9747}
9748
9749static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9750{
9751 // if we were running the route finder, cancel it
9752 vtl->route_finder_started = FALSE;
9753
9754 // ----------------------------------------------------- if current is a route - switch to new track
9755 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9756 {
9757 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9758 if ( a_vik_get_ask_for_create_track_name() ) {
9759 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
9760 if ( !name )
9761 return FALSE;
9762 }
9763 new_track_create_common ( vtl, name );
9764 g_free ( name );
9765 }
9766 return tool_new_track_or_route_click ( vtl, event, vvp );
9767}
9768
9769static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9770{
9771 if ( event->button == 2 ) {
9772 // Pan moving ended - enable potential point drawing again
9773 vtl->draw_sync_do = TRUE;
9774 vtl->draw_sync_done = TRUE;
9775 }
9776}
9777
9778/*** New route ****/
9779
9780static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9781{
9782 return vvp;
9783}
9784
9785static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9786{
9787 // if we were running the route finder, cancel it
9788 vtl->route_finder_started = FALSE;
9789
9790 // -------------------------- if current is a track - switch to new route,
9791 if ( event->button == 1 && ( ! vtl->current_track ||
9792 (vtl->current_track && !vtl->current_track->is_route ) ) )
9793 {
9794 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9795 if ( a_vik_get_ask_for_create_track_name() ) {
9796 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
9797 if ( !name )
9798 return FALSE;
9799 }
9800 new_route_create_common ( vtl, name );
9801 g_free ( name );
9802 }
9803 return tool_new_track_or_route_click ( vtl, event, vvp );
9804}
9805
9806/*** New waypoint ****/
9807
9808static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9809{
9810 return vvp;
9811}
9812
9813static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9814{
9815 VikCoord coord;
9816 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9817 return FALSE;
9818 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9819 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9820 trw_layer_calculate_bounds_waypoints ( vtl );
9821 vik_layer_emit_update ( VIK_LAYER(vtl) );
9822 }
9823 return TRUE;
9824}
9825
9826
9827/*** Edit trackpoint ****/
9828
9829static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9830{
9831 tool_ed_t *t = g_new(tool_ed_t, 1);
9832 t->vvp = vvp;
9833 t->holding = FALSE;
9834 return t;
9835}
9836
9837static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9838{
9839 g_free ( t );
9840}
9841
9842static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9843{
9844 tool_ed_t *t = data;
9845 VikViewport *vvp = t->vvp;
9846 TPSearchParams params;
9847 /* OUTDATED DOCUMENTATION:
9848 find 5 pixel range on each side. then put these UTM, and a pointer
9849 to the winning track name (and maybe the winning track itself), and a
9850 pointer to the winning trackpoint, inside an array or struct. pass
9851 this along, do a foreach on the tracks which will do a foreach on the
9852 trackpoints. */
9853 params.vvp = vvp;
9854 params.x = event->x;
9855 params.y = event->y;
9856 params.closest_track_id = NULL;
9857 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9858 params.closest_tp = NULL;
9859 params.closest_tpl = NULL;
9860 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9861
9862 if ( event->button != 1 )
9863 return FALSE;
9864
9865 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9866 return FALSE;
9867
9868 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9869 return FALSE;
9870
9871 if ( vtl->current_tpl )
9872 {
9873 /* 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.) */
9874 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9875 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9876 if ( !current_tr )
9877 return FALSE;
9878
9879 gint x, y;
9880 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9881
9882 if ( current_tr->visible &&
9883 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9884 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9885 marker_begin_move ( t, event->x, event->y );
9886 return TRUE;
9887 }
9888
9889 }
9890
9891 if ( vtl->tracks_visible )
9892 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
9893
9894 if ( params.closest_tp )
9895 {
9896 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9897 vtl->current_tpl = params.closest_tpl;
9898 vtl->current_tp_id = params.closest_track_id;
9899 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9900 trw_layer_tpwin_init ( vtl );
9901 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9902 vik_layer_emit_update ( VIK_LAYER(vtl) );
9903 return TRUE;
9904 }
9905
9906 if ( vtl->routes_visible )
9907 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
9908
9909 if ( params.closest_tp )
9910 {
9911 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9912 vtl->current_tpl = params.closest_tpl;
9913 vtl->current_tp_id = params.closest_track_id;
9914 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9915 trw_layer_tpwin_init ( vtl );
9916 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9917 vik_layer_emit_update ( VIK_LAYER(vtl) );
9918 return TRUE;
9919 }
9920
9921 /* these aren't the droids you're looking for */
9922 return FALSE;
9923}
9924
9925static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9926{
9927 tool_ed_t *t = data;
9928 VikViewport *vvp = t->vvp;
9929
9930 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9931 return FALSE;
9932
9933 if ( t->holding )
9934 {
9935 VikCoord new_coord;
9936 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9937
9938 /* snap to TP */
9939 if ( event->state & GDK_CONTROL_MASK )
9940 {
9941 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9942 if ( tp && tp != vtl->current_tpl->data )
9943 new_coord = tp->coord;
9944 }
9945 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9946 {
9947 gint x, y;
9948 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9949 marker_moveto ( t, x, y );
9950 }
9951
9952 return TRUE;
9953 }
9954 return FALSE;
9955}
9956
9957static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9958{
9959 tool_ed_t *t = data;
9960 VikViewport *vvp = t->vvp;
9961
9962 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9963 return FALSE;
9964 if ( event->button != 1)
9965 return FALSE;
9966
9967 if ( t->holding ) {
9968 VikCoord new_coord;
9969 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9970
9971 /* snap to TP */
9972 if ( event->state & GDK_CONTROL_MASK )
9973 {
9974 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9975 if ( tp && tp != vtl->current_tpl->data )
9976 new_coord = tp->coord;
9977 }
9978
9979 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9980 if ( vtl->current_tp_track )
9981 vik_track_calculate_bounds ( vtl->current_tp_track );
9982
9983 marker_end_move ( t );
9984
9985 /* diff dist is diff from orig */
9986 if ( vtl->tpwin )
9987 my_tpwin_set_tp ( vtl );
9988
9989 vik_layer_emit_update ( VIK_LAYER(vtl) );
9990 return TRUE;
9991 }
9992 return FALSE;
9993}
9994
9995
9996/*** Extended Route Finder ***/
9997
9998static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9999{
10000 return vvp;
10001}
10002
10003static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10004{
10005 VikCoord *new_end;
10006 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10007 if ( new_end ) {
10008 g_free ( new_end );
10009 vik_layer_emit_update ( VIK_LAYER(vtl) );
10010
10011 /* remove last ' to:...' */
10012 if ( vtl->current_track->comment ) {
10013 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10014 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10015 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10016 last_to - vtl->current_track->comment - 1);
10017 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10018 }
10019 }
10020 }
10021}
10022
10023
10024static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10025{
10026 VikCoord tmp;
10027 if ( !vtl ) return FALSE;
10028 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10029 if ( event->button == 3 && vtl->current_track ) {
10030 tool_extended_route_finder_undo ( vtl );
10031 }
10032 else if ( event->button == 2 ) {
10033 vtl->draw_sync_do = FALSE;
10034 return FALSE;
10035 }
10036 // if we started the track but via undo deleted all the track points, begin again
10037 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10038 return tool_new_track_or_route_click ( vtl, event, vvp );
10039 }
10040 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10041 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10042 struct LatLon start, end;
10043
10044 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10045 vik_coord_to_latlon ( &(tp_start->coord), &start );
10046 vik_coord_to_latlon ( &(tmp), &end );
10047
10048 vtl->route_finder_started = TRUE;
10049 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10050
10051 // update UI to let user know what's going on
10052 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10053 VikRoutingEngine *engine = vik_routing_default_engine ( );
10054 if ( ! engine ) {
10055 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10056 return TRUE;
10057 }
10058 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10059 vik_routing_engine_get_label ( engine ),
10060 start.lat, start.lon, end.lat, end.lon );
10061 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10062 g_free ( msg );
10063 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10064
10065
10066 /* Give GTK a change to display the new status bar before querying the web */
10067 while ( gtk_events_pending ( ) )
10068 gtk_main_iteration ( );
10069
10070 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10071
10072 /* Update UI to say we're done */
10073 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10074 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10075 vik_routing_engine_get_label ( engine ),
10076 start.lat, start.lon, end.lat, end.lon )
10077 : g_strdup_printf ( _("Error getting route from %s."),
10078 vik_routing_engine_get_label ( engine ) );
10079 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10080 g_free ( msg );
10081
10082 vik_layer_emit_update ( VIK_LAYER(vtl) );
10083 } else {
10084 vtl->current_track = NULL;
10085
10086 // create a new route where we will add the planned route to
10087 gboolean ret = tool_new_route_click( vtl, event, vvp );
10088
10089 vtl->route_finder_started = TRUE;
10090
10091 return ret;
10092 }
10093 return TRUE;
10094}
10095
10096static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10097{
10098 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10099 vtl->route_finder_started = FALSE;
10100 vtl->current_track = NULL;
10101 vik_layer_emit_update ( VIK_LAYER(vtl) );
10102 return TRUE;
10103 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10104 tool_extended_route_finder_undo ( vtl );
10105 }
10106 return FALSE;
10107}
10108
10109
10110
10111/*** Show picture ****/
10112
10113static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10114{
10115 return vvp;
10116}
10117
10118/* Params are: vvp, event, last match found or NULL */
10119static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10120{
10121 if ( wp->image && wp->visible )
10122 {
10123 gint x, y, slackx, slacky;
10124 GdkEventButton *event = (GdkEventButton *) params[1];
10125
10126 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10127 slackx = wp->image_width / 2;
10128 slacky = wp->image_height / 2;
10129 if ( x <= event->x + slackx && x >= event->x - slackx
10130 && y <= event->y + slacky && y >= event->y - slacky )
10131 {
10132 params[2] = wp->image; /* we've found a match. however continue searching
10133 * since we want to find the last match -- that
10134 * is, the match that was drawn last. */
10135 }
10136 }
10137}
10138
10139static void trw_layer_show_picture ( menu_array_sublayer values )
10140{
10141 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10142#ifdef WINDOWS
10143 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10144#else /* WINDOWS */
10145 GError *err = NULL;
10146 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10147 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10148 g_free ( quoted_file );
10149 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10150 {
10151 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
10152 g_error_free ( err );
10153 }
10154 g_free ( cmd );
10155#endif /* WINDOWS */
10156}
10157
10158static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10159{
10160 gpointer params[3] = { vvp, event, NULL };
10161 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10162 return FALSE;
10163 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10164 if ( params[2] )
10165 {
10166 static menu_array_sublayer values;
10167 values[MA_VTL] = vtl;
10168 values[MA_MISC] = params[2];
10169 trw_layer_show_picture ( values );
10170 return TRUE; /* found a match */
10171 }
10172 else
10173 return FALSE; /* go through other layers, searching for a match */
10174}
10175
10176/***************************************************************************
10177 ** End tool code
10178 ***************************************************************************/
10179
10180
10181static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10182{
10183 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10184 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10185}
10186
10187/* Structure for thumbnail creating data used in the background thread */
10188typedef struct {
10189 VikTrwLayer *vtl; // Layer needed for redrawing
10190 GSList *pics; // Image list
10191} thumbnail_create_thread_data;
10192
10193static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10194{
10195 guint total = g_slist_length(tctd->pics), done = 0;
10196 while ( tctd->pics )
10197 {
10198 a_thumbnails_create ( (gchar *) tctd->pics->data );
10199 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10200 if ( result != 0 )
10201 return -1; /* Abort thread */
10202
10203 tctd->pics = tctd->pics->next;
10204 }
10205
10206 // Redraw to show the thumbnails as they are now created
10207 if ( IS_VIK_LAYER(tctd->vtl) )
10208 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10209
10210 return 0;
10211}
10212
10213static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10214{
10215 while ( tctd->pics )
10216 {
10217 g_free ( tctd->pics->data );
10218 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10219 }
10220 g_free ( tctd );
10221}
10222
10223void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10224{
10225 if ( ! vtl->has_verified_thumbnails )
10226 {
10227 GSList *pics = NULL;
10228 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10229 if ( pics )
10230 {
10231 gint len = g_slist_length ( pics );
10232 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10233 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10234 tctd->vtl = vtl;
10235 tctd->pics = pics;
10236 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10237 tmp,
10238 (vik_thr_func) create_thumbnails_thread,
10239 tctd,
10240 (vik_thr_free_func) thumbnail_create_thread_free,
10241 NULL,
10242 len );
10243 g_free ( tmp );
10244 }
10245 }
10246}
10247
10248static const gchar* my_track_colors ( gint ii )
10249{
10250 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10251 "#2d870a",
10252 "#135D34",
10253 "#0a8783",
10254 "#0e4d87",
10255 "#05469f",
10256 "#695CBB",
10257 "#2d059f",
10258 "#4a059f",
10259 "#5A171A",
10260 "#96059f"
10261 };
10262 // Fast and reliable way of returning a colour
10263 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10264}
10265
10266static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10267{
10268 GHashTableIter iter;
10269 gpointer key, value;
10270
10271 gint ii = 0;
10272 // Tracks
10273 g_hash_table_iter_init ( &iter, vtl->tracks );
10274
10275 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10276
10277 // Tracks get a random spread of colours if not already assigned
10278 if ( ! VIK_TRACK(value)->has_color ) {
10279 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10280 VIK_TRACK(value)->color = vtl->track_color;
10281 else {
10282 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10283 }
10284 VIK_TRACK(value)->has_color = TRUE;
10285 }
10286
10287 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10288
10289 ii++;
10290 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10291 ii = 0;
10292 }
10293
10294 // Routes
10295 ii = 0;
10296 g_hash_table_iter_init ( &iter, vtl->routes );
10297
10298 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10299
10300 // Routes get an intermix of reds
10301 if ( ! VIK_TRACK(value)->has_color ) {
10302 if ( ii )
10303 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10304 else
10305 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10306 VIK_TRACK(value)->has_color = TRUE;
10307 }
10308
10309 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10310
10311 ii = !ii;
10312 }
10313}
10314
10315/*
10316 * (Re)Calculate the bounds of the waypoints in this layer,
10317 * This should be called whenever waypoints are changed
10318 */
10319void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10320{
10321 struct LatLon topleft = { 0.0, 0.0 };
10322 struct LatLon bottomright = { 0.0, 0.0 };
10323 struct LatLon ll;
10324
10325 GHashTableIter iter;
10326 gpointer key, value;
10327
10328 g_hash_table_iter_init ( &iter, vtl->waypoints );
10329
10330 // Set bounds to first point
10331 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10332 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10333 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10334 }
10335
10336 // Ensure there is another point...
10337 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10338
10339 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10340
10341 // See if this point increases the bounds.
10342 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10343
10344 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10345 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10346 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10347 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10348 }
10349 }
10350
10351 vtl->waypoints_bbox.north = topleft.lat;
10352 vtl->waypoints_bbox.east = bottomright.lon;
10353 vtl->waypoints_bbox.south = bottomright.lat;
10354 vtl->waypoints_bbox.west = topleft.lon;
10355}
10356
10357static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10358{
10359 vik_track_calculate_bounds ( trk );
10360}
10361
10362static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10363{
10364 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10365 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10366}
10367
10368static void trw_layer_sort_all ( VikTrwLayer *vtl )
10369{
10370 if ( ! VIK_LAYER(vtl)->vt )
10371 return;
10372
10373 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10374 if ( g_hash_table_size (vtl->tracks) > 1 )
10375 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10376
10377 if ( g_hash_table_size (vtl->routes) > 1 )
10378 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10379
10380 if ( g_hash_table_size (vtl->waypoints) > 1 )
10381 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10382}
10383
10384static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10385{
10386 if ( VIK_LAYER(vtl)->realized )
10387 trw_layer_verify_thumbnails ( vtl, vvp );
10388 trw_layer_track_alloc_colors ( vtl );
10389
10390 trw_layer_calculate_bounds_waypoints ( vtl );
10391 trw_layer_calculate_bounds_tracks ( vtl );
10392
10393 // Apply treeview sort after loading all the tracks for this layer
10394 // (rather than sorted insert on each individual track additional)
10395 // and after subsequent changes to the properties as the specified order may have changed.
10396 // since the sorting of a treeview section is now very quick
10397 // NB sorting is also performed after every name change as well to maintain the list order
10398 trw_layer_sort_all ( vtl );
10399
10400 // Setting metadata time if not otherwise set
10401 if ( vtl->metadata ) {
10402
10403 gboolean need_to_set_time = TRUE;
10404 if ( vtl->metadata->timestamp ) {
10405 need_to_set_time = FALSE;
10406 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10407 need_to_set_time = TRUE;
10408 }
10409
10410 if ( need_to_set_time ) {
10411 // Could rewrite this as a general get first time of a TRW Layer function
10412 GTimeVal timestamp;
10413 timestamp.tv_usec = 0;
10414 gboolean has_timestamp = FALSE;
10415
10416 GList *gl = NULL;
10417 gl = g_hash_table_get_values ( vtl->tracks );
10418 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10419 gl = g_list_first ( gl );
10420
10421 // Check times of tracks
10422 if ( gl ) {
10423 // Only need to check the first track as they have been sorted by time
10424 VikTrack *trk = (VikTrack*)gl->data;
10425 // Assume trackpoints already sorted by time
10426 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10427 if ( tpt && tpt->has_timestamp ) {
10428 timestamp.tv_sec = tpt->timestamp;
10429 has_timestamp = TRUE;
10430 }
10431 g_list_free ( gl );
10432 }
10433
10434 if ( !has_timestamp ) {
10435 // 'Last' resort - current time
10436 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10437 g_get_current_time ( &timestamp );
10438
10439 // Check times of waypoints
10440 gl = g_hash_table_get_values ( vtl->waypoints );
10441 GList *iter;
10442 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10443 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10444 if ( wpt->has_timestamp ) {
10445 if ( timestamp.tv_sec > wpt->timestamp ) {
10446 timestamp.tv_sec = wpt->timestamp;
10447 has_timestamp = TRUE;
10448 }
10449 }
10450 }
10451 g_list_free ( gl );
10452 }
10453
10454 vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10455 }
10456 }
10457}
10458
10459VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10460{
10461 return vtl->coord_mode;
10462}
10463
10464/**
10465 * Uniquify the whole layer
10466 * Also requires the layers panel as the names shown there need updating too
10467 * Returns whether the operation was successful or not
10468 */
10469gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10470{
10471 if ( vtl && vlp ) {
10472 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10473 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10474 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10475 return TRUE;
10476 }
10477 return FALSE;
10478}
10479
10480static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10481{
10482 vik_coord_convert ( &(wp->coord), *dest_mode );
10483}
10484
10485static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10486{
10487 vik_track_convert ( tr, *dest_mode );
10488}
10489
10490static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10491{
10492 if ( vtl->coord_mode != dest_mode )
10493 {
10494 vtl->coord_mode = dest_mode;
10495 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10496 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10497 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10498 }
10499}
10500
10501static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10502{
10503 vtl->menu_selection = selection;
10504}
10505
10506static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10507{
10508 return (vtl->menu_selection);
10509}
10510
10511/* ----------- Downloading maps along tracks --------------- */
10512
10513static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10514{
10515 /* TODO: calculating based on current size of viewport */
10516 const gdouble w_at_zoom_0_125 = 0.0013;
10517 const gdouble h_at_zoom_0_125 = 0.0011;
10518 gdouble zoom_factor = zoom_level/0.125;
10519
10520 wh->lat = h_at_zoom_0_125 * zoom_factor;
10521 wh->lon = w_at_zoom_0_125 * zoom_factor;
10522
10523 return 0; /* all OK */
10524}
10525
10526static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10527{
10528 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10529 (dist->lat >= ABS(to->north_south - from->north_south)))
10530 return NULL;
10531
10532 VikCoord *coord = g_malloc(sizeof(VikCoord));
10533 coord->mode = VIK_COORD_LATLON;
10534
10535 if (ABS(gradient) < 1) {
10536 if (from->east_west > to->east_west)
10537 coord->east_west = from->east_west - dist->lon;
10538 else
10539 coord->east_west = from->east_west + dist->lon;
10540 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10541 } else {
10542 if (from->north_south > to->north_south)
10543 coord->north_south = from->north_south - dist->lat;
10544 else
10545 coord->north_south = from->north_south + dist->lat;
10546 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10547 }
10548
10549 return coord;
10550}
10551
10552static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10553{
10554 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10555 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10556
10557 VikCoord *next = from;
10558 while (TRUE) {
10559 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10560 break;
10561 list = g_list_prepend(list, next);
10562 }
10563
10564 return list;
10565}
10566
10567void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10568{
10569 typedef struct _Rect {
10570 VikCoord tl;
10571 VikCoord br;
10572 VikCoord center;
10573 } Rect;
10574#define GLRECT(iter) ((Rect *)((iter)->data))
10575
10576 struct LatLon wh;
10577 GList *rects_to_download = NULL;
10578 GList *rect_iter;
10579
10580 if (get_download_area_width(vvp, zoom_level, &wh))
10581 return;
10582
10583 GList *iter = tr->trackpoints;
10584 if (!iter)
10585 return;
10586
10587 gboolean new_map = TRUE;
10588 VikCoord *cur_coord, tl, br;
10589 Rect *rect;
10590 while (iter) {
10591 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10592 if (new_map) {
10593 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10594 rect = g_malloc(sizeof(Rect));
10595 rect->tl = tl;
10596 rect->br = br;
10597 rect->center = *cur_coord;
10598 rects_to_download = g_list_prepend(rects_to_download, rect);
10599 new_map = FALSE;
10600 iter = iter->next;
10601 continue;
10602 }
10603 gboolean found = FALSE;
10604 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10605 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10606 found = TRUE;
10607 break;
10608 }
10609 }
10610 if (found)
10611 iter = iter->next;
10612 else
10613 new_map = TRUE;
10614 }
10615
10616 GList *fillins = NULL;
10617 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10618 /* seems that ATM the function get_next_coord works only for LATLON */
10619 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10620 /* fill-ins for far apart points */
10621 GList *cur_rect, *next_rect;
10622 for (cur_rect = rects_to_download;
10623 (next_rect = cur_rect->next) != NULL;
10624 cur_rect = cur_rect->next) {
10625 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10626 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10627 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10628 }
10629 }
10630 } else
10631 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10632
10633 if (fillins) {
10634 GList *fiter = fillins;
10635 while (fiter) {
10636 cur_coord = (VikCoord *)(fiter->data);
10637 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10638 rect = g_malloc(sizeof(Rect));
10639 rect->tl = tl;
10640 rect->br = br;
10641 rect->center = *cur_coord;
10642 rects_to_download = g_list_prepend(rects_to_download, rect);
10643 fiter = fiter->next;
10644 }
10645 }
10646
10647 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10648 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10649 }
10650
10651 if (fillins) {
10652 for (iter = fillins; iter; iter = iter->next)
10653 g_free(iter->data);
10654 g_list_free(fillins);
10655 }
10656 if (rects_to_download) {
10657 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10658 g_free(rect_iter->data);
10659 g_list_free(rects_to_download);
10660 }
10661}
10662
10663static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10664{
10665 VikMapsLayer *vml;
10666 gint selected_map;
10667 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10668 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10669 gint selected_zoom, default_zoom;
10670
10671 VikTrwLayer *vtl = values[MA_VTL];
10672 VikLayersPanel *vlp = values[MA_VLP];
10673 VikTrack *trk;
10674 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10675 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10676 else
10677 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10678 if ( !trk )
10679 return;
10680
10681 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10682
10683 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10684 int num_maps = g_list_length(vmls);
10685
10686 if (!num_maps) {
10687 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10688 return;
10689 }
10690
10691 // Convert from list of vmls to list of names. Allowing the user to select one of them
10692 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10693 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10694
10695 gchar **np = map_names;
10696 VikMapsLayer **lp = map_layers;
10697 int i;
10698 for (i = 0; i < num_maps; i++) {
10699 vml = (VikMapsLayer *)(vmls->data);
10700 *lp++ = vml;
10701 *np++ = vik_maps_layer_get_map_label(vml);
10702 vmls = vmls->next;
10703 }
10704 // Mark end of the array lists
10705 *lp = NULL;
10706 *np = NULL;
10707
10708 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10709 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10710 if (cur_zoom == zoom_vals[default_zoom])
10711 break;
10712 }
10713 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10714
10715 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10716 goto done;
10717
10718 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10719
10720done:
10721 for (i = 0; i < num_maps; i++)
10722 g_free(map_names[i]);
10723 g_free(map_names);
10724 g_free(map_layers);
10725
10726 g_list_free(vmls);
10727
10728}
10729
10730/**** lowest waypoint number calculation ***/
10731static gint highest_wp_number_name_to_number(const gchar *name) {
10732 if ( strlen(name) == 3 ) {
10733 int n = atoi(name);
10734 if ( n < 100 && name[0] != '0' )
10735 return -1;
10736 if ( n < 10 && name[0] != '0' )
10737 return -1;
10738 return n;
10739 }
10740 return -1;
10741}
10742
10743
10744static void highest_wp_number_reset(VikTrwLayer *vtl)
10745{
10746 vtl->highest_wp_number = -1;
10747}
10748
10749static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10750{
10751 /* if is bigger that top, add it */
10752 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10753 if ( new_wp_num > vtl->highest_wp_number )
10754 vtl->highest_wp_number = new_wp_num;
10755}
10756
10757static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10758{
10759 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10760 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10761 if ( vtl->highest_wp_number == old_wp_num ) {
10762 gchar buf[4];
10763 vtl->highest_wp_number--;
10764
10765 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10766 /* search down until we find something that *does* exist */
10767
10768 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10769 vtl->highest_wp_number--;
10770 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10771 }
10772 }
10773}
10774
10775/* get lowest unused number */
10776static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10777{
10778 gchar buf[4];
10779 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10780 return NULL;
10781 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10782 return g_strdup(buf);
10783}
10784
10785/**
10786 * trw_layer_create_track_list_both:
10787 *
10788 * Create the latest list of tracks and routes
10789 */
10790static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10791{
10792 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10793 GList *tracks = NULL;
10794 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10795 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10796
10797 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10798}
10799
10800static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10801{
10802 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10803
10804 gchar *title = NULL;
10805 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10806 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10807 else
10808 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10809
10810 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10811 g_free ( title );
10812}
10813
10814static void trw_layer_track_list_dialog ( menu_array_layer values )
10815{
10816 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10817
10818 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10819 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10820 g_free ( title );
10821}
10822
10823static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10824{
10825 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10826
10827 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10828 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
10829 g_free ( title );
10830}