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