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