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