2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
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>
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.
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.
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
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 */
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"
47 #include "garminsymbols.h"
48 #include "thumbnails.h"
49 #include "background.h"
55 #include "geonamessearch.h"
56 #ifdef VIK_CONFIG_OPENSTREETMAP
57 #include "osm-traces.h"
60 #include "datasources.h"
61 #include "datasource_gps.h"
62 #include "vikexttool_datasources.h"
66 #include "vikrouting.h"
68 #include "icons/icons.h"
82 #include <gdk/gdkkeysyms.h>
84 #include <glib/gstdio.h>
85 #include <glib/gi18n.h>
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
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
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
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. */
114 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
116 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
118 FS_XX_SMALL = 0, // 'xx-small'
121 FS_MEDIUM, // DEFAULT
128 struct _VikTrwLayer {
131 GHashTable *tracks_iters;
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;
140 gboolean track_draw_labels;
143 guint8 drawpoints_size;
144 guint8 drawelevation;
145 guint8 elevation_factor;
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;
156 VikTRWMetadata *metadata;
158 PangoLayout *tracklabellayout;
159 font_size_t track_font_size;
160 gchar *track_fsize_str;
164 gboolean wp_draw_symbols;
165 font_size_t wp_font_size;
167 vik_layer_sort_order_t wp_sort_order;
169 gdouble track_draw_speed_factor;
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;
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;
188 VikCoordMode coord_mode;
190 /* wp editing tool */
191 VikWaypoint *current_wp;
192 gpointer current_wp_id;
194 gboolean waypoint_rightclick;
196 /* track editing tool */
198 VikTrack *current_tp_track;
199 gpointer current_tp_id;
200 VikTrwLayerTpwin *tpwin;
202 /* track editing tool -- more specifically, moving tps */
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;
216 guint16 image_cache_size;
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
221 gboolean has_verified_thumbnails;
223 GtkMenu *wp_right_click_menu;
224 GtkMenu *track_right_click_menu;
227 VikStdLayerMenuItem menu_selection;
229 gint highest_wp_number;
232 GtkWidget *tracks_analysis_dialog;
235 /* A caached waypoint image. */
238 gchar *image; /* filename */
241 struct DrawingParams {
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;
256 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
261 MA_SUBTYPE, // OR END for Layer only
270 typedef gpointer menu_array_layer[2];
271 typedef gpointer menu_array_sublayer[MA_LAST];
273 static void trw_layer_delete_item ( menu_array_sublayer values );
274 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
277 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
278 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
280 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
283 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
286 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
287 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292 static void trw_layer_goto_track_center ( menu_array_sublayer values );
293 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295 static void trw_layer_merge_with_other ( menu_array_sublayer values );
296 static void trw_layer_append_track ( menu_array_sublayer values );
297 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300 static void trw_layer_split_segments ( menu_array_sublayer values );
301 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304 static void trw_layer_reverse ( menu_array_sublayer values );
305 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307 static void trw_layer_show_picture ( menu_array_sublayer values );
308 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
310 static void trw_layer_centerize ( menu_array_layer values );
311 static void trw_layer_auto_view ( menu_array_layer values );
312 static void trw_layer_goto_wp ( menu_array_layer values );
313 static void trw_layer_new_wp ( menu_array_layer values );
314 static void trw_layer_new_track ( menu_array_layer values );
315 static void trw_layer_new_route ( menu_array_layer values );
316 static void trw_layer_finish_track ( menu_array_layer values );
317 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318 static void trw_layer_auto_tracks_view ( menu_array_layer values );
319 static void trw_layer_delete_all_tracks ( menu_array_layer values );
320 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
325 #ifdef VIK_CONFIG_GEOTAG
326 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328 static void trw_layer_geotagging_track ( menu_array_sublayer values );
329 static void trw_layer_geotagging ( menu_array_layer values );
331 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333 static void trw_layer_acquire_url_cb ( menu_array_layer values );
334 #ifdef VIK_CONFIG_OPENSTREETMAP
335 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
338 #ifdef VIK_CONFIG_GEOCACHES
339 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
341 #ifdef VIK_CONFIG_GEOTAG
342 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
344 static void trw_layer_acquire_file_cb ( menu_array_layer values );
345 static void trw_layer_gps_upload ( menu_array_layer values );
347 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
348 static void trw_layer_track_list_dialog ( menu_array_layer values );
349 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
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
354 static void trw_layer_auto_routes_view ( menu_array_layer values );
355 static void trw_layer_delete_all_routes ( menu_array_layer values );
356 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
359 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
364 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
365 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
367 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
372 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
379 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
389 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
391 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
397 static void cached_pixbuf_free ( CachedPixbuf *cp );
398 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
400 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
403 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
404 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
406 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
407 static void highest_wp_number_reset(VikTrwLayer *vtl);
408 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
409 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
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
417 static 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,
422 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
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 },
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 },
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 },
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,
455 (VikToolMouseFunc) tool_edit_waypoint_click,
456 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
457 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
459 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
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,
465 (VikToolMouseFunc) tool_edit_trackpoint_click,
466 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
467 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
469 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
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,
475 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
480 TOOL_CREATE_WAYPOINT=0,
485 TOOL_EDIT_TRACKPOINT,
490 /****** PARAMETERS ******/
492 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
493 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
495 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
496 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
498 #define MIN_POINT_SIZE 2
499 #define MAX_POINT_SIZE 10
501 #define MIN_ARROW_SIZE 3
502 #define MAX_ARROW_SIZE 20
504 static 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
521 static gchar* params_font_sizes[] = {
522 N_("Extra Extra Small"),
528 N_("Extra Extra Large"),
531 // Needs to align with vik_layer_sort_order_t
532 static gchar* params_sort_order[] = {
534 N_("Name Ascending"),
535 N_("Name Descending"),
539 static VikLayerParamData black_color_default ( void ) {
540 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
542 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
543 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
544 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
545 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
546 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
547 static VikLayerParamData trackbgcolor_default ( void ) {
548 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
550 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
551 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
552 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
554 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
555 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
556 static VikLayerParamData wptextcolor_default ( void ) {
557 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
559 static VikLayerParamData wpbgcolor_default ( void ) {
560 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
562 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
563 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
565 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
566 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
567 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
569 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
571 static VikLayerParamData string_default ( void )
573 VikLayerParamData data;
578 VikLayerParam 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 },
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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
601 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_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, ¶ms_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 },
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, ¶ms_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 },
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, ¶ms_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, ¶ms_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, ¶ms_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
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 },
629 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
631 // Sublayer visibilities
679 *** 1) Add to trw_layer_params and enumeration
680 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
683 /****** END PARAMETERS ******/
685 /* Layer Interface function definitions */
686 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
687 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
688 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
689 static void trw_layer_free ( VikTrwLayer *trwlayer );
690 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
691 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
692 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
693 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
694 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
695 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
696 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
697 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
698 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
699 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
700 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
701 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
702 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
703 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
704 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
705 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
706 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
707 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
708 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
709 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
710 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
711 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
712 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
713 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
714 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
715 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
716 /* End Layer Interface function definitions */
718 VikLayerInterface vik_trw_layer_interface = {
725 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
729 params_groups, /* params_groups */
730 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
734 (VikLayerFuncCreate) trw_layer_create,
735 (VikLayerFuncRealize) trw_layer_realize,
736 (VikLayerFuncPostRead) trw_layer_post_read,
737 (VikLayerFuncFree) trw_layer_free,
739 (VikLayerFuncProperties) NULL,
740 (VikLayerFuncDraw) trw_layer_draw,
741 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
743 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
744 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
746 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
747 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
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,
755 (VikLayerFuncMarshall) trw_layer_marshall,
756 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
758 (VikLayerFuncSetParam) trw_layer_set_param,
759 (VikLayerFuncGetParam) trw_layer_get_param,
760 (VikLayerFuncChangeParam) trw_layer_change_param,
762 (VikLayerFuncReadFileData) a_gpspoint_read_file,
763 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
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,
771 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
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,
779 static gboolean have_diary_program = FALSE;
780 static gchar *diary_program = NULL;
781 #define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
783 static gboolean have_geojson_export = FALSE;
785 static gboolean have_astro_program = FALSE;
786 static gchar *astro_program = NULL;
787 #define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
789 // NB Only performed once per program run
790 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
792 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
794 //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
795 diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
797 diary_program = g_strdup ( "rednotebook" );
801 // User specified so assume it works
802 have_diary_program = TRUE;
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!!
813 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
815 g_warning ("Diary: stderr: %s", mystderr );
817 gchar **tokens = NULL;
818 if ( mystdout && g_strcmp0(mystdout, "") )
819 tokens = g_strsplit(mystdout, " ", 0);
821 tokens = g_strsplit(mystderr, " ", 0);
824 gchar *token = tokens[num];
825 while ( token && num < 2 ) {
827 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
828 have_diary_program = TRUE;
833 g_strfreev ( tokens );
840 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
841 have_geojson_export = TRUE;
845 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
847 //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
848 astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
850 astro_program = g_strdup ( "stellarium" );
854 // User specified so assume it works
855 have_astro_program = TRUE;
857 if ( g_find_program_in_path( astro_program ) ) {
858 have_astro_program = TRUE;
862 GType vik_trw_layer_get_type ()
864 static GType vtl_type = 0;
868 static const GTypeInfo vtl_info =
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),
878 NULL /* instance init */
880 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
885 VikTRWMetadata *vik_trw_metadata_new()
887 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
890 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
895 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
897 return vtl->metadata;
900 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
903 vik_trw_metadata_free ( vtl->metadata );
904 vtl->metadata = metadata;
909 const gchar *date_str;
911 const VikWaypoint *wpt;
916 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
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)));
924 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
933 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
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)));
941 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
951 * Find an item by date
953 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
957 df.date_str = date_str;
962 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
964 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
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 );
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 );
977 vik_layer_emit_update ( VIK_LAYER(vtl) );
982 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
984 static menu_array_sublayer values;
990 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
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
998 trw_layer_delete_item ( values );
1001 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
1003 static menu_array_sublayer values;
1009 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
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
1017 trw_layer_copy_item_cb(values);
1018 trw_layer_cut_item_cb(values);
1021 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
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;
1029 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
1033 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1034 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
1035 if ( wp && wp->name )
1038 name = NULL; // Broken :(
1040 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1041 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1042 if ( trk && trk->name )
1045 name = NULL; // Broken :(
1048 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1049 if ( trk && trk->name )
1052 name = NULL; // Broken :(
1055 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1056 subtype, len, name, data);
1060 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
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);
1067 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1069 // Slightly cheating method, routing via the panels capability
1070 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1073 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1083 GByteArray *ba = g_byte_array_new ();
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 );
1090 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1093 g_byte_array_append ( ba, id, il );
1101 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1108 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
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);
1119 trw_layer_calculate_bounds_waypoints ( vtl );
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) );
1126 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
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);
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) );
1142 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
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);
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) );
1161 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1168 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
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;
1191 case PARAM_DM: vtl->drawmode = data.u; break;
1193 vtl->track_color = data.c;
1194 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1196 case PARAM_DP: vtl->drawpoints = data.b; break;
1198 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1199 vtl->drawpoints_size = data.u;
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;
1206 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1207 vtl->drawdirections_size = data.u;
1209 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1210 vtl->stop_length = data.u;
1212 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1213 vtl->elevation_factor = data.u;
1215 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1217 vtl->line_thickness = data.u;
1218 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1221 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1223 vtl->bg_line_thickness = data.u;
1224 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
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));
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 )
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 ();
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 ) );
1250 vtl->waypoint_color = data.c;
1251 if ( vtl->waypoint_gc )
1252 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
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));
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));
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 );
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;
1287 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
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;
1298 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1300 VikLayerParamData rv;
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;
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;
1348 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1350 // This '-3' is to account for the first few parameters not in the properties
1351 const gint OFFSET = -3;
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.
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 );
1374 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
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 );
1398 // Alter sensitivity of all track colours according to the draw track mode.
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 );
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 );
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
1423 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
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 ( );
1437 guint object_length;
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) \
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 );
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 );
1456 // Now sublayer data
1457 GHashTableIter iter;
1458 gpointer key, value;
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 );
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 );
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 );
1490 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1492 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1494 gint consumed_length;
1496 // First the overall layer parameters
1497 memcpy(&pl, data, sizeof(pl));
1499 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1502 consumed_length = pl;
1503 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1505 #define tlm_size (*(gint *)data)
1506 // See marshalling above for order of how this is written
1508 data += sizeof_len_and_subtype + tlm_size;
1510 // Now the individual sublayers:
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 ) {
1518 // Reuse pl to read the subtype from the data stream
1519 memcpy(&pl, data+sizeof(gint), sizeof(pl));
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 );
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 );
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 );
1540 consumed_length += tlm_size + sizeof_len_and_subtype;
1543 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1545 // Not stored anywhere else so need to regenerate
1546 trw_layer_calculate_bounds_waypoints ( vtl );
1551 // Keep interesting hash function at least visible
1553 static guint strcase_hash(gconstpointer v)
1555 // 31 bit hash function
1558 gchar s[128]; // malloc is too slow for reading big files
1561 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1562 p[i] = toupper(t[i]);
1568 for (p += 1; *p != '\0'; p++)
1569 h = (h << 5) - h + *p;
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
1578 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
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 );
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
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.
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
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 );
1602 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1604 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
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;
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
1619 static void trw_layer_free ( VikTrwLayer *trwlayer )
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);
1628 /* ODC: replace with GArray */
1629 trw_layer_free_track_gcs ( trwlayer );
1631 if ( trwlayer->wp_right_click_menu )
1632 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1634 if ( trwlayer->track_right_click_menu )
1635 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1637 if ( trwlayer->tracklabellayout != NULL)
1638 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1640 if ( trwlayer->wplabellayout != NULL)
1641 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1643 if ( trwlayer->waypoint_gc != NULL )
1644 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1646 if ( trwlayer->waypoint_text_gc != NULL )
1647 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1649 if ( trwlayer->waypoint_bg_gc != NULL )
1650 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1652 g_free ( trwlayer->wp_fsize_str );
1653 g_free ( trwlayer->track_fsize_str );
1655 if ( trwlayer->tpwin != NULL )
1656 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1658 if ( trwlayer->tracks_analysis_dialog != NULL )
1659 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1661 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1662 g_queue_free ( trwlayer->image_cache );
1665 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
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
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;
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 */
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;
1705 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
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
1715 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
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;
1726 return VIK_TRW_LAYER_TRACK_GC_AVER;
1729 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1732 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
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 );
1741 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1743 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1745 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1746 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1748 // Fallback if parse failure
1749 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1751 g_free ( label_markup );
1753 gint label_x, label_y;
1755 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
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 );
1762 * distance_in_preferred_units:
1763 * @dist: The source distance in standard SI Units (i.e. metres)
1765 * TODO: This is a generic function that could be moved into globals.c or utils.c
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
1771 * Returns: The distance in the units as specified by the preferences
1773 static gdouble distance_in_preferred_units ( gdouble dist )
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);
1781 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1782 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1784 // VIK_UNITS_DISTANCE_KILOMETRES:
1786 mydist = dist/1000.0;
1793 * trw_layer_draw_dist_labels:
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.
1798 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
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};
1804 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1806 // Convert to specified unit to find the friendly breakdown value
1807 dist = distance_in_preferred_units ( dist );
1811 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1812 if ( chunksd[i] > dist ) {
1814 dist = chunksd[index];
1819 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1821 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1822 gdouble dist_i = dist * i;
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);
1829 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1830 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1832 // VIK_UNITS_DISTANCE_KILOMETRES:
1834 dist_i = dist_i*1000.0;
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 );
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;
1849 if ( tp_current && tp_next ) {
1850 // Construct the name based on the distance value
1853 switch (dist_units) {
1854 case VIK_UNITS_DISTANCE_MILES:
1855 units = g_strdup ( _("miles") );
1857 // VIK_UNITS_DISTANCE_KILOMETRES:
1859 units = g_strdup ( _("km") );
1863 // Convert for display
1864 dist_i = distance_in_preferred_units ( dist_i );
1866 // Make the precision of the output related to the unit size.
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);
1872 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
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 );
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 };
1885 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1888 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1889 fgcolour = gdk_color_to_string ( &(trk->color) );
1891 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1893 // if highlight mode on, then colour the background in the highlight colour
1895 if ( drawing_highlight )
1896 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1898 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1900 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1902 g_free ( fgcolour );
1903 g_free ( bgcolour );
1910 * trw_layer_draw_track_name_labels:
1912 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1914 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1917 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1918 fgcolour = gdk_color_to_string ( &(trk->color) );
1920 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1922 // if highlight mode on, then colour the background in the highlight colour
1924 if ( drawing_highlight )
1925 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1927 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1929 gchar *ename = g_markup_escape_text ( trk->name, -1 );
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;
1938 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1940 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1943 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1944 // No other labels to draw
1947 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1950 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1953 VikCoord begin_coord = tp_begin->coord;
1954 VikCoord end_coord = tp_end->coord;
1956 gboolean done_start_end = FALSE;
1958 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1959 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
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
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);
1972 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1974 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1975 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1978 done_start_end = TRUE;
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 );
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 );
2002 g_free ( fgcolour );
2003 g_free ( bgcolour );
2007 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
2009 if ( ! track->visible )
2012 /* TODO: this function is a mess, get rid of any redundancy */
2013 GList *list = track->trackpoints;
2015 gboolean useoldvals = TRUE;
2017 gboolean drawpoints;
2019 gboolean drawelevation;
2020 gdouble min_alt, max_alt, alt_diff = 0;
2022 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
2023 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
2026 if ( dp->vtl->drawelevation )
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;
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 );
2037 if ( draw_track_outline )
2038 drawpoints = drawstops = FALSE;
2040 drawpoints = dp->vtl->drawpoints;
2041 drawstops = dp->vtl->drawstops;
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;
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;
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;
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);
2074 int x, y, oldx, oldy;
2075 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2077 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2079 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2081 // Draw the first point as something a bit different from the normal points
2082 // ATM it's slightly bigger and a triangle
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 );
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));
2102 while ((list = g_list_next(list)))
2104 tp = VIK_TRACKPOINT(list->data);
2105 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
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 ) )
2113 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2116 * If points are the same in display coordinates, don't draw.
2118 if ( useoldvals && x == oldx && y == oldy )
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 );
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 ) );
2136 if ( drawpoints && ! draw_track_outline )
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
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 );
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 );
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 );
2160 if ((!tp->newsegment) && (dp->vtl->drawlines))
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);
2168 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2170 if ( draw_track_outline ) {
2171 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2175 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2177 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2179 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2184 tmp[1].y = oldy-FIXALTITUDE(list->data);
2186 tmp[2].y = y-FIXALTITUDE(list->next->data);
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];
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);
2197 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
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;
2208 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2209 // Avoid divide by zero and ensure at least 1 pixel big
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) );
2224 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
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 )
2229 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
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 ));
2236 * If points are the same in display coordinates, don't draw.
2238 if ( x != oldx || y != oldy )
2240 if ( draw_track_outline )
2241 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2243 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2249 * If points are the same in display coordinates, don't draw.
2251 if ( x != oldx && y != oldy )
2253 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2254 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
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 );
2268 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2269 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2275 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2277 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2278 trw_layer_draw_track ( id, track, dp, FALSE );
2282 static void cached_pixbuf_free ( CachedPixbuf *cp )
2284 g_object_unref ( G_OBJECT(cp->pixbuf) );
2285 g_free ( cp->image );
2288 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2290 return strcmp ( cp->image, name );
2293 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
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 ) )
2301 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2303 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2305 if ( wp->image && dp->vtl->drawimages )
2307 GdkPixbuf *pixbuf = NULL;
2310 if ( dp->vtl->image_alpha == 0)
2313 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2315 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2318 gchar *image = wp->image;
2319 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2320 if ( ! regularthumb )
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. */
2327 CachedPixbuf *cp = NULL;
2328 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2329 if ( dp->vtl->image_size == 128 )
2330 cp->pixbuf = regularthumb;
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) );
2337 cp->image = g_strdup ( image );
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 );
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 ) );
2348 pixbuf = cp->pixbuf;
2352 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2358 w = gdk_pixbuf_get_width ( pixbuf );
2359 h = gdk_pixbuf_get_height ( pixbuf );
2361 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
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 );
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 );
2375 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2377 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
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 );
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 );
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;
2406 if ( dp->vtl->drawlabels )
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;
2411 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
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 );
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 );
2419 // Fallback if parse failure
2420 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2422 g_free ( wp_label_markup );
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;
2429 label_y = y - dp->vtl->wp_size - height - 2;
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);
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 );
2441 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2443 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2444 trw_layer_draw_waypoint ( id, wp, dp );
2448 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2450 static struct DrawingParams dp;
2451 g_assert ( l != NULL );
2453 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2455 if ( l->tracks_visible )
2456 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2458 if ( l->routes_visible )
2459 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2461 if (l->waypoints_visible)
2462 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2465 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
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 )
2473 trw_layer_draw_with_highlight ( l, data, FALSE );
2476 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
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)) )
2481 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2485 * vik_trw_layer_draw_highlight_item:
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)
2490 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
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)) )
2496 static struct DrawingParams dp;
2497 init_drawing_params ( &dp, vtl, vvp, TRUE );
2500 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2502 trw_layer_draw_track_cb ( NULL, trk, &dp );
2504 if ( vtl->waypoints_visible && wpt ) {
2505 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2510 * vik_trw_layer_draw_highlight_item:
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)
2516 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
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)) )
2522 static struct DrawingParams dp;
2523 init_drawing_params ( &dp, vtl, vvp, TRUE );
2526 gboolean is_routes = (trks == vtl->routes);
2527 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2529 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2532 if ( vtl->waypoints_visible && wpts )
2533 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2536 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2539 if ( vtl->track_bg_gc )
2541 g_object_unref ( vtl->track_bg_gc );
2542 vtl->track_bg_gc = NULL;
2544 if ( vtl->track_1color_gc )
2546 g_object_unref ( vtl->track_1color_gc );
2547 vtl->track_1color_gc = NULL;
2549 if ( vtl->current_track_gc )
2551 g_object_unref ( vtl->current_track_gc );
2552 vtl->current_track_gc = NULL;
2554 if ( vtl->current_track_newpoint_gc )
2556 g_object_unref ( vtl->current_track_newpoint_gc );
2557 vtl->current_track_newpoint_gc = NULL;
2560 if ( ! vtl->track_gc )
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;
2568 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2570 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2571 gint width = vtl->line_thickness;
2573 if ( vtl->track_gc )
2574 trw_layer_free_track_gcs ( vtl );
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 );
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;
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 );
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 );
2595 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
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
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
2604 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2606 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2609 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2611 VikTrwLayer *rv = trw_layer_new1 ( vp );
2612 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2614 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2615 /* early exit, as the rest is GUI related */
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);
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);
2625 trw_layer_new_track_gcs ( rv, vp );
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 );
2632 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2634 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2639 #define SMALL_ICON_SIZE 18
2641 * Can accept a null symbol, and may return null value
2643 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
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 );
2653 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2655 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2657 GdkPixbuf *pixbuf = NULL;
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);
2668 gdk_pixbuf_fill ( pixbuf, pixel );
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 );
2674 g_object_unref (pixbuf);
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 );
2680 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2682 if ( ! track->visible )
2683 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2686 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2688 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
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 );
2692 *new_iter = *((GtkTreeIter *) pass_along[1]);
2693 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2695 if ( ! wp->visible )
2696 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2699 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2701 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2704 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2706 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2709 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2711 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2714 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2717 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2719 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2720 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2722 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2724 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2727 if ( g_hash_table_size (vtl->routes) > 0 ) {
2728 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2730 pass_along[0] = &(vtl->routes_iter);
2731 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2733 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2735 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2738 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2739 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2741 pass_along[0] = &(vtl->waypoints_iter);
2742 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2744 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2746 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2751 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
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:
2760 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2762 return (t->visible ^= 1);
2766 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2768 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2770 return (t->visible ^= 1);
2774 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2776 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2778 return (t->visible ^= 1);
2788 * Return a property about tracks for this layer
2790 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2792 return vtl->line_thickness;
2795 // Structure to hold multiple track information for a layer
2804 * Build up layer multiple track information via updating the tooltip_tracks structure
2806 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2808 tt->length = tt->length + vik_track_get_length (tr);
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 ) {
2816 t1 = vik_track_get_tp_first(tr)->timestamp;
2817 t2 = trkpt_last->timestamp;
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 )
2826 // Update find the earliest / last times
2827 if ( t1 < tt->start_time )
2828 tt->start_time = t1;
2829 if ( t2 > tt->end_time )
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);
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)
2846 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2857 static gchar tmp_buf[128];
2860 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
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 );
2867 GDate* gdate_start = g_date_new ();
2868 g_date_set_time_t (gdate_start, tt.start_time);
2870 GDate* gdate_end = g_date_new ();
2871 g_date_set_time_t (gdate_end, tt.end_time);
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);
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);
2886 if ( tt.length > 0.0 ) {
2887 gdouble len_in_units;
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);
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);
2900 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2901 len_in_units = tt.length/1000.0;
2905 // Timing information if available
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));
2912 g_snprintf (tbuf2, sizeof(tbuf2),
2913 _("\n%sTotal Length %.1f %s%s"),
2914 tbuf3, len_in_units, tbuf4, tbuf1);
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);
2922 g_date_free (gdate_start);
2923 g_date_free (gdate_end);
2930 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2934 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2936 // Very simple tooltip - may expand detail in the future...
2937 static gchar tmp_buf[32];
2938 g_snprintf (tmp_buf, sizeof(tmp_buf),
2940 g_hash_table_size (l->tracks));
2944 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2946 // Very simple tooltip - may expand detail in the future...
2947 static gchar tmp_buf[32];
2948 g_snprintf (tmp_buf, sizeof(tmp_buf),
2950 g_hash_table_size (l->routes));
2955 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2956 // Same tooltip for a route
2957 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2960 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2961 tr = g_hash_table_lookup ( l->tracks, sublayer );
2963 tr = g_hash_table_lookup ( l->routes, sublayer );
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 );
2979 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
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);
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);
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);
3001 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3003 // Very simple tooltip - may expand detail in the future...
3004 static gchar tmp_buf[32];
3005 g_snprintf (tmp_buf, sizeof(tmp_buf),
3007 g_hash_table_size (l->waypoints));
3011 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3013 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
3014 // NB It's OK to return NULL
3019 return w->description;
3028 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
3031 * set_statusbar_msg_info_trkpt:
3033 * Function to show track point information on the statusbar
3034 * Items displayed is controlled by the settings format code
3036 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
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" );
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 );
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 );
3056 g_free ( statusbar_format_code );
3060 * Function to show basic waypoint information on the statusbar
3062 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
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)));
3070 //VIK_UNITS_HEIGHT_METRES:
3071 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
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 );
3082 // Combine parts to make overall message
3085 // Add comment if available
3086 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
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 );
3096 * General layer selection function, find out which bit is selected and take appropriate action
3098 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3101 l->current_wp = NULL;
3102 l->current_wp_id = NULL;
3103 trw_layer_cancel_current_tp ( l, FALSE );
3106 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3110 case VIK_TREEVIEW_TYPE_LAYER:
3112 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3113 /* Mark for redraw */
3118 case VIK_TREEVIEW_TYPE_SUBLAYER:
3122 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3124 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3125 /* Mark for redraw */
3129 case VIK_TRW_LAYER_SUBLAYER_TRACK:
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 */
3137 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3139 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3140 /* Mark for redraw */
3144 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
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 */
3152 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3154 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3155 /* Mark for redraw */
3159 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3161 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
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 */
3173 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3182 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3187 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3192 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3197 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3199 return l->waypoints;
3202 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3204 return vtl->tracks_iters;
3207 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3209 return vtl->routes_iters;
3212 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3214 return vtl->waypoints;
3217 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3219 return ! ( g_hash_table_size ( vtl->tracks ) ||
3220 g_hash_table_size ( vtl->routes ) ||
3221 g_hash_table_size ( vtl->waypoints ) );
3224 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3226 return vtl->tracks_visible;
3229 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3231 return vtl->routes_visible;
3234 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3236 return vtl->waypoints_visible;
3240 * ATM use a case sensitive find
3241 * Finds the first one
3243 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3245 if ( wp && wp->name )
3246 if ( ! strcmp ( wp->name, name ) )
3252 * Get waypoint by name - not guaranteed to be unique
3253 * Finds the first one
3255 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3257 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3261 * ATM use a case sensitive find
3262 * Finds the first one
3264 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3266 if ( trk && trk->name )
3267 if ( ! strcmp ( trk->name, name ) )
3273 * Get track by name - not guaranteed to be unique
3274 * Finds the first one
3276 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3278 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3282 * Get route by name - not guaranteed to be unique
3283 * Finds the first one
3285 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3287 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3290 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
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;
3302 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
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 );
3314 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
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)
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 );
3329 static void trw_layer_centerize ( menu_array_layer values )
3331 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3333 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3334 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3336 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3339 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
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 };
3346 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3347 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
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;
3354 minmin.lat = maxmin[1].lat;
3356 struct LatLon maxmax;
3357 if ( maxmin[0].lon > maxmin[1].lon )
3358 maxmax.lon = maxmin[0].lon;
3360 maxmax.lon = maxmin[1].lon;
3362 /* Never zoom in too far - generally not that useful, as too close ! */
3363 /* Always recalculate the 'best' zoom level */
3365 vik_viewport_set_zoom ( vvp, zoom );
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 */
3384 vik_viewport_set_zoom ( vvp, zoom );
3388 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
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)
3396 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3401 static void trw_layer_auto_view ( menu_array_layer values )
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 );
3409 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3412 static void trw_layer_export_gpspoint ( menu_array_layer values )
3414 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3416 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3418 g_free ( auto_save_name );
3421 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3423 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3425 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3427 g_free ( auto_save_name );
3430 static void trw_layer_export_gpx ( menu_array_layer values )
3432 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3434 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3436 g_free ( auto_save_name );
3439 static void trw_layer_export_kml ( menu_array_layer values )
3441 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3443 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3445 g_free ( auto_save_name );
3448 static void trw_layer_export_geojson ( menu_array_layer values )
3450 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3452 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3454 g_free ( auto_save_name );
3457 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
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 );
3463 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3465 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3468 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3470 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3473 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3475 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
3480 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3482 if ( !trk || !trk->name )
3485 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3487 gchar *label = NULL;
3488 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3489 label = _("Export Route as GPX");
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 );
3494 g_free ( auto_save_name );
3497 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3499 wpu_udata *user_data = udata;
3500 if ( wp == user_data->wp ) {
3501 user_data->uuid = id;
3507 static void trw_layer_goto_wp ( menu_array_layer values )
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,
3515 GTK_RESPONSE_REJECT,
3517 GTK_RESPONSE_ACCEPT,
3520 GtkWidget *label, *entry;
3521 label = gtk_label_new(_("Waypoint Name:"));
3522 entry = gtk_entry_new();
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 );
3529 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3531 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
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 );
3538 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3541 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3542 vik_layers_panel_emit_update ( vlp );
3544 // Find and select on the side panel
3549 // Hmmm, want key of it
3550 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
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 );
3563 gtk_widget_destroy ( dia );
3566 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3568 gchar *default_name = highest_wp_number_get(vtl);
3569 VikWaypoint *wp = vik_waypoint_new();
3570 gchar *returned_name;
3572 wp->coord = *def_coord;
3574 // Attempt to auto set height if DEM data is available
3575 vik_waypoint_apply_dem_data ( wp, TRUE );
3577 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3579 if ( returned_name )
3582 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3583 g_free (default_name);
3584 g_free (returned_name);
3587 g_free (default_name);
3588 vik_waypoint_free(wp);
3592 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
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);
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 );
3607 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
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} };
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 );
3619 #ifdef VIK_CONFIG_GEOTAG
3620 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3622 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3624 // Update directly - not changing the mtime
3625 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3628 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3630 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3633 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3637 * Use code in separate file for this feature as reasonably complex
3639 static void trw_layer_geotagging_track ( menu_array_sublayer values )
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;
3646 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3652 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3654 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3655 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3657 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3663 static void trw_layer_geotagging ( menu_array_layer values )
3665 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3666 // Unset so can be reverified later if necessary
3667 vtl->has_verified_thumbnails = FALSE;
3669 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3676 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3678 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
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);
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 );
3692 * Acquire into this TRW Layer straight from GPS Device
3694 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3696 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3700 * Acquire into this TRW Layer from Directions
3702 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3704 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3708 * Acquire into this TRW Layer from an entered URL
3710 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3712 trw_layer_acquire ( values, &vik_datasource_url_interface );
3715 #ifdef VIK_CONFIG_OPENSTREETMAP
3717 * Acquire into this TRW Layer from OSM
3719 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3721 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3725 * Acquire into this TRW Layer from OSM for 'My' Traces
3727 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3729 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3733 #ifdef VIK_CONFIG_GEOCACHES
3735 * Acquire into this TRW Layer from Geocaching.com
3737 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3739 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3743 #ifdef VIK_CONFIG_GEOTAG
3745 * Acquire into this TRW Layer from images
3747 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3749 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3751 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3753 // Reverify thumbnails as they may have changed
3754 vtl->has_verified_thumbnails = FALSE;
3755 trw_layer_verify_thumbnails ( vtl, NULL );
3760 * Acquire into this TRW Layer from any GPS Babel supported file
3762 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3764 trw_layer_acquire ( values, &vik_datasource_file_interface );
3767 static void trw_layer_gps_upload ( menu_array_layer values )
3769 menu_array_sublayer data;
3771 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3773 data[MA_VTL] = values[MA_VTL];
3774 data[MA_VLP] = values[MA_VLP];
3776 trw_layer_gps_upload_any ( data );
3780 * If pass_along[3] is defined that this will upload just that track
3782 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3784 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3785 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
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;
3792 if ( values[MA_SUBTYPE] ) {
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] );
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] );
3802 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3805 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3809 else if ( !values[MA_CONFIRM] )
3810 xfer_all = TRUE; // i.e. whole layer
3812 if (track && !track->visible) {
3813 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3817 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3818 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3819 GTK_DIALOG_DESTROY_WITH_PARENT,
3821 GTK_RESPONSE_ACCEPT,
3823 GTK_RESPONSE_REJECT,
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 );
3833 gtk_widget_grab_focus ( response_w );
3835 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3837 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3838 datasource_gps_clean_up ( dgs );
3839 gtk_widget_destroy ( dialog );
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 );
3852 gtk_widget_destroy ( dialog );
3854 // When called from the viewport - work the corresponding layerspanel:
3856 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3859 // Apply settings to transfer to the GPS device
3866 vik_layers_panel_get_viewport (vlp),
3874 static void trw_layer_new_wp ( menu_array_layer values )
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 );
3886 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
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;
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 );
3900 static void trw_layer_new_track ( menu_array_layer values )
3902 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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 );
3909 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3913 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
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 );
3925 static void trw_layer_new_route ( menu_array_layer values )
3927 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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 );
3933 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3937 static void trw_layer_auto_routes_view ( menu_array_layer values )
3939 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3940 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
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 );
3951 static void trw_layer_finish_track ( menu_array_layer values )
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) );
3959 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3961 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3962 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
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 );
3972 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3974 /* NB do not care if wp is visible or not */
3975 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3978 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3980 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3981 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
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 );
3988 /* If at least 2 waypoints - find center and then zoom to fit */
3989 else if ( g_hash_table_size (vtl->waypoints) > 1 )
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 );
3999 vik_layers_panel_emit_update ( vlp );
4002 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
4004 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
4007 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
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);
4015 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
4017 static menu_array_layer pass_along;
4019 GtkWidget *export_submenu;
4020 pass_along[MA_VTL] = vtl;
4021 pass_along[MA_VLP] = vlp;
4023 item = gtk_menu_item_new();
4024 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4025 gtk_widget_show ( item );
4027 if ( vtl->current_track ) {
4028 if ( vtl->current_track->is_route )
4029 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
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 );
4037 item = gtk_menu_item_new ();
4038 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4039 gtk_widget_show ( item );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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);
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 );
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) );
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) );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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);
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 );
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 );
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 );
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 );
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 );
4247 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
4316 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4317 gtk_widget_show ( item );
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 );
4323 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4324 gtk_widget_show ( item );
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)) );
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)) );
4342 // Fake Waypoint UUIDs vi simple increasing integer
4343 static guint wp_uuid = 0;
4345 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4349 vik_waypoint_set_name (wp, name);
4351 if ( VIK_LAYER(vtl)->realized )
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) );
4358 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
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 );
4363 // Actual setting of visibility dependent on the waypoint
4364 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4366 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
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 );
4372 highest_wp_number_add_wp(vtl, name);
4373 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4377 // Fake Track UUIDs vi simple increasing integer
4378 static guint tr_uuid = 0;
4380 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4384 vik_track_set_name (t, name);
4386 if ( VIK_LAYER(vtl)->realized )
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) );
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 );
4397 // Actual setting of visibility dependent on the track
4398 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4400 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
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 );
4406 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4408 trw_layer_update_treeview ( vtl, t );
4411 // Fake Route UUIDs vi simple increasing integer
4412 static guint rt_uuid = 0;
4414 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4418 vik_track_set_name (t, name);
4420 if ( VIK_LAYER(vtl)->realized )
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) );
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 );
4433 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
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 );
4439 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4441 trw_layer_update_treeview ( vtl, t );
4444 /* to be called whenever a track has been deleted or may have been changed. */
4445 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4447 if (vtl->current_tp_track == trk )
4448 trw_layer_cancel_current_tp ( vtl, FALSE );
4452 * Normally this is done to due the waypoint size preference having changed
4454 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4456 GHashTableIter iter;
4457 gpointer key, value;
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);
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 );
4473 * trw_layer_new_unique_sublayer_name:
4475 * Allocates a unique new name
4477 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4480 gchar *newname = g_strdup(name);
4485 switch ( sublayer_type ) {
4486 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4487 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4489 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4490 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4493 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4496 // If found a name already in use try adding 1 to it and we try again
4498 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4500 newname = new_newname;
4503 } while ( id != NULL);
4508 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
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 );
4515 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4517 if ( vtl->route_finder_append && vtl->current_track ) {
4518 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
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 ),
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 */
4534 // No more uniqueness of name forced when loading from a file
4536 vik_trw_layer_add_route ( vtl, name, tr );
4538 vik_trw_layer_add_track ( vtl, name, tr );
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;
4547 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4549 *l = g_list_append(*l, id);
4553 * Move an item from one TRW layer to another TRW layer
4555 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4557 // TODO reconsider strategy when moving within layer (if anything...)
4558 gboolean rename = ( vtl_src != vtl_dest );
4562 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4563 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4567 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4569 newname = g_strdup ( trk->name );
4571 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4572 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4574 vik_trw_layer_delete_track ( vtl_src, trk );
4577 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4578 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4582 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4584 newname = g_strdup ( trk->name );
4586 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4587 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4589 vik_trw_layer_delete_route ( vtl_src, trk );
4592 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4593 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4597 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4599 newname = g_strdup ( wp->name );
4601 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4602 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4604 trw_layer_delete_waypoint ( vtl_src, wp );
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 );
4612 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4614 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4615 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4617 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4618 GList *items = NULL;
4621 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4622 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4624 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4625 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4627 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4628 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
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);
4638 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4645 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4646 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4650 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4652 trku_udata *user_data = udata;
4653 if ( trk == user_data->trk ) {
4654 user_data->uuid = id;
4660 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4662 gboolean was_visible = FALSE;
4663 if ( trk && trk->name ) {
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;
4673 was_visible = trk->visible;
4675 if ( trk == vtl->route_finder_added_track )
4676 vtl->route_finder_added_track = NULL;
4682 // Hmmm, want key of it
4683 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4685 if ( trkf && udata.uuid ) {
4686 /* could be current_tp, so we have to check */
4687 trw_layer_cancel_tps_of_track ( vtl, trk );
4689 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
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 );
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) );
4701 // Incase it was selected (no item delete signal ATM)
4702 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4708 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4710 gboolean was_visible = FALSE;
4712 if ( trk && trk->name ) {
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;
4721 was_visible = trk->visible;
4723 if ( trk == vtl->route_finder_added_track )
4724 vtl->route_finder_added_track = NULL;
4730 // Hmmm, want key of it
4731 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4733 if ( trkf && udata.uuid ) {
4734 /* could be current_tp, so we have to check */
4735 trw_layer_cancel_tps_of_track ( vtl, trk );
4737 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
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 );
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) );
4749 // Incase it was selected (no item delete signal ATM)
4750 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4756 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4758 gboolean was_visible = FALSE;
4760 if ( wp && wp->name ) {
4762 if ( wp == vtl->current_wp ) {
4763 vtl->current_wp = NULL;
4764 vtl->current_wp_id = NULL;
4765 vtl->moving_wp = FALSE;
4768 was_visible = wp->visible;
4774 // Hmmm, want key of it
4775 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4777 if ( wpf && udata.uuid ) {
4778 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4781 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4782 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4784 highest_wp_number_remove_wp(vtl, wp->name);
4785 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
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) );
4792 // Incase it was selected (no item delete signal ATM)
4793 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4801 // Only for temporary use by trw_layer_delete_waypoint_by_name
4802 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4804 wpu_udata *user_data = udata;
4805 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4806 user_data->uuid = id;
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
4817 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
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
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 );
4829 vik_waypoint_free (udata.wp);
4831 if ( wpf && udata.uuid )
4832 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4838 VikTrack *trk; // input
4839 gpointer uuid; // output
4842 // Only for temporary use by trw_layer_delete_track_by_name
4843 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4845 tpu_udata *user_data = udata;
4846 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4847 user_data->uuid = id;
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
4858 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
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
4867 // Hmmm, want key of it
4868 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4870 vik_track_free (udata.trk);
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 ));
4884 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4886 vik_treeview_item_delete (vt, it );
4889 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
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);
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);
4901 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4903 vik_layer_emit_update ( VIK_LAYER(vtl) );
4906 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
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);
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);
4918 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4920 vik_layer_emit_update ( VIK_LAYER(vtl) );
4923 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4925 vtl->current_wp = NULL;
4926 vtl->current_wp_id = NULL;
4927 vtl->moving_wp = FALSE;
4929 highest_wp_number_reset(vtl);
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);
4935 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4937 vik_layer_emit_update ( VIK_LAYER(vtl) );
4940 static void trw_layer_delete_all_tracks ( menu_array_layer values )
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);
4950 static void trw_layer_delete_all_routes ( menu_array_layer values )
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);
4960 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
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);
4970 static void trw_layer_delete_item ( menu_array_sublayer values )
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 )
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\"?"),
4985 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4986 trw_layer_calculate_bounds_waypoints ( vtl );
4989 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
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\"?"),
4999 was_visible = vik_trw_layer_delete_track ( vtl, trk );
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\"?"),
5012 was_visible = vik_trw_layer_delete_route ( vtl, trk );
5016 vik_layer_emit_update ( VIK_LAYER(vtl) );
5020 * Rename waypoint and maintain corresponding name of waypoint in the treeview
5022 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
5024 vik_waypoint_set_name ( wp, new_name );
5026 // Now update the treeview as well
5031 // Need key of it for treeview update
5032 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5034 if ( wpf && udataU.uuid ) {
5035 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
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 );
5045 * Maintain icon of waypoint in the treeview
5047 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5049 // update the treeview
5054 // Need key of it for treeview update
5055 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5057 if ( wpf && udataU.uuid ) {
5058 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5061 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5066 static void trw_layer_properties_item ( menu_array_sublayer values )
5068 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5069 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5071 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5073 if ( wp && wp->name )
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 );
5078 trw_layer_waypoint_rename ( vtl, wp, new_name );
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) );
5083 if ( updated && VIK_LAYER(vtl)->visible )
5084 vik_layer_emit_update ( VIK_LAYER(vtl) );
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] );
5093 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5095 if ( tr && tr->name )
5097 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5108 * trw_layer_track_statistics:
5110 * Show track statistics.
5111 * ATM jump to the stats page in the properties
5112 * TODO: consider separating the stats into an individual dialog?
5114 static void trw_layer_track_statistics ( menu_array_sublayer values )
5116 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
5121 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5123 if ( trk && trk->name ) {
5124 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5134 * Update the treeview of the track id - primarily to update the icon
5136 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5142 gpointer *trkf = NULL;
5143 if ( trk->is_route )
5144 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5146 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5148 if ( trkf && udata.uuid ) {
5150 GtkTreeIter *iter = NULL;
5151 if ( trk->is_route )
5152 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5154 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
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);
5171 Parameter 1 -> VikLayersPanel
5172 Parameter 2 -> VikLayer
5173 Parameter 3 -> VikViewport
5175 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
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) );
5182 /* since vlp not set, vl & vvp should be valid instead! */
5184 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5185 vik_layer_emit_update ( VIK_LAYER(vl) );
5190 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5192 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5197 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5199 if ( track && track->trackpoints )
5200 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5203 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5205 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5210 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5212 if ( track && track->trackpoints )
5214 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
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);
5224 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5226 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5231 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
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 ) ) ) {
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 ) )
5248 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5251 trk_copy->is_route = !trk_copy->is_route;
5253 // ATM can't set name to self - so must create temporary copy
5254 gchar *name = g_strdup ( trk_copy->name );
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 );
5262 // Extra route conversion bits...
5263 vik_track_merge_segments ( trk_copy );
5264 vik_track_to_routepoints ( trk_copy );
5266 vik_trw_layer_delete_track ( vtl, trk );
5267 vik_trw_layer_add_route ( vtl, name, trk_copy );
5271 // Update in case color of track / route changes when moving between sublayers
5272 vik_layer_emit_update ( VIK_LAYER(vtl) );
5275 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5277 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5282 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5285 vik_track_anonymize_times ( track );
5288 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5290 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
5295 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
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);
5303 if ( track->trackpoints )
5304 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5308 * extend a track using route finder
5310 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5312 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5313 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
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;
5321 if ( track->trackpoints )
5322 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5328 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5330 // If have a vlp then perform a basic test to see if any DEM info available...
5332 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
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.") );
5343 * apply_dem_data_common:
5345 * A common function for applying the DEM values and reporting the results.
5347 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5349 if ( !trw_layer_dem_test ( vtl, vlp ) )
5352 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5353 // Inform user how much was changed
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);
5360 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5362 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5367 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5370 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5373 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5375 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5380 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5383 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5389 * A common function for applying the elevation smoothing and reporting the results.
5391 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5393 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5394 // Inform user how much was changed
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);
5404 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5406 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5411 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5416 smooth_it ( vtl, track, FALSE );
5419 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5421 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5426 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5431 smooth_it ( vtl, track, TRUE );
5435 * Commonal helper function
5437 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
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);
5445 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5447 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5448 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5450 if ( !trw_layer_dem_test ( vtl, vlp ) )
5454 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5456 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5458 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5462 GHashTableIter iter;
5463 gpointer key, value;
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 );
5471 wp_changed_message ( vtl, changed );
5474 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5476 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5477 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5479 if ( !trw_layer_dem_test ( vtl, vlp ) )
5483 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5485 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5487 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5491 GHashTableIter iter;
5492 gpointer key, value;
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 );
5500 wp_changed_message ( vtl, changed );
5503 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5505 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5510 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5514 if ( !track->trackpoints )
5516 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5519 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5521 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5526 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5531 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5534 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5537 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5539 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5544 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5549 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5552 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5555 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5557 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
5562 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5567 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5570 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5574 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5576 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5578 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
5583 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5585 if ( trk && trk->trackpoints )
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]) );
5593 vik_layer_emit_update ( VIK_LAYER(vtl) );
5598 * Refine the selected track/route with a routing engine.
5599 * The routing engine is selected by the user, when requestiong the job.
5601 static void trw_layer_route_refine ( menu_array_sublayer values )
5603 static gint last_engine = 0;
5604 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
5610 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5612 if ( trk && trk->trackpoints )
5614 /* Check size of the route */
5615 int nb = vik_track_get_tp_count(trk);
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?"),
5623 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5624 gtk_widget_destroy ( dialog );
5625 if (response != GTK_RESPONSE_OK )
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,
5633 GTK_RESPONSE_REJECT,
5635 GTK_RESPONSE_ACCEPT,
5637 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5638 gtk_widget_show_all(label);
5640 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
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);
5646 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5648 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5650 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
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);
5657 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5659 /* Force saving track */
5660 /* FIXME: remove or rename this hack */
5661 vtl->route_finder_check_added_track = TRUE;
5664 vik_routing_engine_refine (routing, vtl, trk);
5666 /* FIXME: remove or rename this hack */
5667 if ( vtl->route_finder_added_track )
5668 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5670 vtl->route_finder_added_track = NULL;
5671 vtl->route_finder_check_added_track = FALSE;
5673 vik_layer_emit_update ( VIK_LAYER(vtl) );
5675 /* Restore cursor */
5676 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5678 gtk_widget_destroy ( dialog );
5682 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5684 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5685 trw_layer_tpwin_init ( vtl );
5688 /*************************************
5689 * merge/split by time routines
5690 *************************************/
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).
5701 gboolean with_timestamps;
5703 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5705 twt_udata *user_data = udata;
5706 VikTrackpoint *p1, *p2;
5707 VikTrack *trk = VIK_TRACK(value);
5708 if (trk == user_data->exclude) {
5712 if (trk->trackpoints) {
5713 p1 = vik_track_get_tp_first(trk);
5714 p2 = vik_track_get_tp_last(trk);
5716 if ( user_data->with_timestamps ) {
5717 if (!p1->has_timestamp || !p2->has_timestamp) {
5722 // Don't add tracks with timestamps when getting non timestamp tracks
5723 if (p1->has_timestamp || p2->has_timestamp) {
5729 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5733 * find_nearby_tracks_by_time:
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]
5739 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5741 VikTrack *trk = VIK_TRACK(value);
5743 GList **nearby_tracks = ((gpointer *)user_data)[0];
5744 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5746 if ( !orig_trk || !orig_trk->trackpoints )
5750 * detect reasons for not merging, and return
5751 * if no reason is found not to merge, then do it.
5754 twt_udata *udata = user_data;
5755 // Exclude the original track from the compiled list
5756 if (trk == udata->exclude) {
5760 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5761 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5763 if (trk->trackpoints) {
5765 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5766 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5768 if (!p1->has_timestamp || !p2->has_timestamp) {
5769 //g_print("no timestamp\n");
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 ||
5777 abs(p1->timestamp - t2) < threshold)
5784 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5787 /* comparison function used to sort tracks; a and b are hash table keys */
5788 /* Not actively used - can be restored if needed
5789 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5791 GHashTable *tracks = user_data;
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;
5797 if (t1 < t2) return -1;
5798 if (t1 > t2) return 1;
5803 /* comparison function used to sort trackpoints */
5804 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5806 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5808 if (t1 < t2) return -1;
5809 if (t1 > t2) return 1;
5814 * comparison function which can be used to sort tracks or waypoints by name
5816 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5818 const gchar* namea = (const gchar*) a;
5819 const gchar* nameb = (const gchar*) b;
5820 if ( namea == NULL || nameb == NULL)
5823 // Same sort method as used in the vik_treeview_*_alphabetize functions
5824 return strcmp ( namea, nameb );
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
5832 static void trw_layer_merge_with_other ( menu_array_sublayer values )
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;
5840 ght_tracks = vtl->tracks;
5842 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5847 if ( !track->trackpoints )
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;
5857 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5858 other_tracks = g_list_reverse(other_tracks);
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"));
5864 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
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 );
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 );
5878 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5880 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
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);
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 );
5896 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5899 vik_track_steal_and_append_trackpoints ( track, merge_track );
5900 if ( track->is_route )
5901 vik_trw_layer_delete_route (vtl, merge_track);
5903 vik_trw_layer_delete_track (vtl, merge_track);
5904 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5907 for (l = merge_list; l != NULL; l = g_list_next(l))
5909 g_list_free(merge_list);
5911 vik_layer_emit_update( VIK_LAYER(vtl) );
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)
5917 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5919 twt_udata *user_data = udata;
5922 if (trk == user_data->exclude) {
5926 // Sort named list alphabetically
5927 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
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
5935 static void trw_layer_append_track ( menu_array_sublayer values )
5938 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5940 GHashTable *ght_tracks;
5941 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5942 ght_tracks = vtl->routes;
5944 ght_tracks = vtl->tracks;
5946 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5951 GList *other_tracks_names = NULL;
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...
5957 udata.result = &other_tracks_names;
5958 udata.exclude = trk;
5960 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
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),
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") );
5972 g_list_free(other_tracks_names);
5974 // It's a list, but shouldn't contain more than one other track!
5975 if ( append_list ) {
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 );
5984 append_track = vik_trw_layer_get_track ( vtl, l->data );
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);
5991 vik_trw_layer_delete_track (vtl, append_track);
5994 for (l = append_list; l != NULL; l = g_list_next(l))
5996 g_list_free(append_list);
5998 vik_layer_emit_update( VIK_LAYER(vtl) );
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
6008 static void trw_layer_append_other ( menu_array_sublayer values )
6011 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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;
6019 ght_mykind = vtl->tracks;
6020 ght_others = vtl->routes;
6023 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6028 GList *other_tracks_names = NULL;
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...
6034 udata.result = &other_tracks_names;
6035 udata.exclude = trk;
6037 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
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),
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") );
6049 g_list_free(other_tracks_names);
6051 // It's a list, but shouldn't contain more than one other track!
6052 if ( append_list ) {
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...
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 );
6063 append_track = vik_trw_layer_get_route ( vtl, l->data );
6065 if ( append_track ) {
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 ) ) ) {
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 );
6081 vik_track_steal_and_append_trackpoints ( trk, append_track );
6083 // Delete copied which is FROM THE OTHER TYPE list
6084 if ( trk->is_route )
6085 vik_trw_layer_delete_track (vtl, append_track);
6087 vik_trw_layer_delete_route (vtl, append_track);
6090 for (l = append_list; l != NULL; l = g_list_next(l))
6092 g_list_free(append_list);
6093 vik_layer_emit_update( VIK_LAYER(vtl) );
6097 /* merge by segments */
6098 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
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:
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 );
6111 /* merge by time routine */
6112 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6114 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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"));
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);
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"));
6137 g_list_free(tracks_with_timestamp);
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)) {
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;
6151 static gpointer params[3];
6153 while ( attempt_merge ) {
6155 // Don't try again unless tracks have changed
6156 attempt_merge = FALSE;
6158 trps = orig_trk->trackpoints;
6162 if (nearby_tracks) {
6163 g_list_free(nearby_tracks);
6164 nearby_tracks = NULL;
6167 params[0] = &nearby_tracks;
6168 params[1] = orig_trk;
6169 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6171 /* get a list of adjacent-in-time tracks */
6172 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6175 GList *l = nearby_tracks;
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));
6181 // Tracks have changed, therefore retry again against all the remaining tracks
6182 attempt_merge = TRUE;
6187 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6190 g_list_free(nearby_tracks);
6192 vik_layer_emit_update( VIK_LAYER(vtl) );
6196 * Split a track at the currently selected trackpoint
6198 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6200 if ( !vtl->current_tpl )
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);
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;
6213 vtl->current_tpl->next->prev = newglist; /* end old track here */
6214 vtl->current_tpl->next = NULL;
6216 // Bounds of the selected track changed due to the split
6217 vik_track_calculate_bounds ( vtl->current_tp_track );
6219 vtl->current_tpl = newglist; /* change tp to first of new track. */
6220 vtl->current_tp_track = tr;
6223 vik_trw_layer_add_route ( vtl, name, tr );
6225 vik_trw_layer_add_track ( vtl, name, tr );
6227 // Bounds of the new track created by the split
6228 vik_track_calculate_bounds ( tr );
6234 // Also need id of newly created track
6237 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6239 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6241 if ( trkf && udata.uuid )
6242 vtl->current_tp_id = udata.uuid;
6244 vtl->current_tp_id = NULL;
6246 vik_layer_emit_update(VIK_LAYER(vtl));
6252 /* split by time routine */
6253 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
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;
6259 GList *newlists = NULL;
6260 GList *newtps = NULL;
6261 static guint thr = 1;
6268 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6269 _("Split Threshold..."),
6270 _("Split when time between trackpoints exceeds:"),
6275 /* iterate through trackpoints, and copy them into new lists without touching original list */
6276 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6280 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6282 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
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?"),
6289 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6294 if (ts - prev_ts > thr*60) {
6295 /* flush accumulated trackpoints into new list */
6296 newlists = g_list_append(newlists, g_list_reverse(newtps));
6300 /* accumulate trackpoint copies in newtps, in reverse order */
6301 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6303 iter = g_list_next(iter);
6306 newlists = g_list_append(newlists, g_list_reverse(newtps));
6309 /* put lists of trackpoints into tracks */
6311 // Only bother updating if the split results in new tracks
6312 if (g_list_length (newlists) > 1) {
6317 tr = vik_track_copy ( track, FALSE );
6318 tr->trackpoints = (GList *)(iter->data);
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);
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));
6330 g_list_free(newlists);
6334 * Split a track by the number of points as specified by the user
6336 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6338 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6343 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6348 // Check valid track
6349 GList *trps = track->trackpoints;
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
6360 // Was a valid number returned?
6366 GList *newlists = NULL;
6367 GList *newtps = NULL;
6372 /* accumulate trackpoint copies in newtps, in reverse order */
6373 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6375 if (count >= points) {
6376 /* flush accumulated trackpoints into new list */
6377 newlists = g_list_append(newlists, g_list_reverse(newtps));
6381 iter = g_list_next(iter);
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
6387 newlists = g_list_append(newlists, g_list_reverse(newtps));
6390 /* put lists of trackpoints into tracks */
6392 // Only bother updating if the split results in new tracks
6393 if (g_list_length (newlists) > 1) {
6398 tr = vik_track_copy ( track, FALSE );
6399 tr->trackpoints = (GList *)(iter->data);
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);
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);
6409 g_free ( new_tr_name );
6410 vik_track_calculate_bounds ( tr );
6412 iter = g_list_next(iter);
6414 // Remove original track and then update the display
6415 if ( track->is_route )
6416 vik_trw_layer_delete_route (vtl, track);
6418 vik_trw_layer_delete_track (vtl, track);
6419 vik_layer_emit_update(VIK_LAYER(vtl));
6421 g_list_free(newlists);
6425 * Split a track at the currently selected trackpoint
6427 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
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 );
6435 * Split a track by its segments
6436 * Routes do not have segments so don't call this for routes
6438 static void trw_layer_split_segments ( menu_array_sublayer values )
6440 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6441 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6448 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6451 for ( i = 0; i < ntracks; 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 );
6460 // Remove original track
6461 vik_trw_layer_delete_track ( vtl, trk );
6462 vik_layer_emit_update ( VIK_LAYER(vtl) );
6465 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6468 /* end of split/merge routines */
6470 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
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 */
6479 // Delete current trackpoint
6480 vik_trackpoint_free ( vtl->current_tpl->data );
6481 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6483 // Set to current to the available adjacent trackpoint
6484 vtl->current_tpl = new_tpl;
6486 if ( vtl->current_tp_track ) {
6487 vik_track_calculate_bounds ( vtl->current_tp_track );
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 );
6499 * Delete the selected point
6501 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6503 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6508 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6513 if ( !vtl->current_tpl )
6516 trw_layer_trackpoint_selected_delete ( vtl, trk );
6518 // Track has been updated so update tps:
6519 trw_layer_cancel_tps_of_track ( vtl, trk );
6521 vik_layer_emit_update ( VIK_LAYER(vtl) );
6525 * Delete adjacent track points at the same position
6526 * AKA Delete Dulplicates on the Properties Window
6528 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6530 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6535 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6540 gulong removed = vik_track_remove_dup_points ( trk );
6542 // Track has been updated so update tps:
6543 trw_layer_cancel_tps_of_track ( vtl, trk );
6545 // Inform user how much was deleted as it's not obvious from the normal view
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);
6551 vik_layer_emit_update ( VIK_LAYER(vtl) );
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
6558 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6560 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6565 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6570 gulong removed = vik_track_remove_same_time_points ( trk );
6572 // Track has been updated so update tps:
6573 trw_layer_cancel_tps_of_track ( vtl, trk );
6575 // Inform user how much was deleted as it's not obvious from the normal view
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);
6581 vik_layer_emit_update ( VIK_LAYER(vtl) );
6587 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6589 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6594 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6599 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6601 vik_layer_emit_update ( VIK_LAYER(vtl) );
6604 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6606 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6611 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6616 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6618 vik_layer_emit_update ( VIK_LAYER(vtl) );
6624 static void trw_layer_reverse ( menu_array_sublayer values )
6626 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
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] );
6631 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6636 vik_track_reverse ( track );
6638 vik_layer_emit_update ( VIK_LAYER(vtl) );
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
6647 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
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 );
6659 * Open a diary at the date of the track or waypoint
6661 static void trw_layer_diary ( menu_array_sublayer values )
6663 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
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 );
6677 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
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] );
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 );
6691 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
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
6701 static 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 )
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 );
6714 util_add_to_deletion_list ( tmp );
6719 // Format of stellarium lat & lon seems designed to be particularly awkward
6720 // who uses ' & " in the parameters for the command line?!
6723 static gchar *convert_to_dms ( gdouble dec )
6729 gchar *result = NULL;
6743 tmp = (tmp - val_d) * 60;
6747 val_s = (tmp - val_m) * 60;
6750 result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
6755 * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
6757 static void trw_layer_astro ( menu_array_sublayer values )
6759 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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] );
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);
6777 if ( tp->has_timestamp ) {
6779 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
6781 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
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 );
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);
6793 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
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] );
6800 if ( wpt->has_timestamp ) {
6802 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
6804 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
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 );
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 );
6816 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6821 * Similar to trw_layer_enum_item, but this uses a sorted method
6824 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
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);
6834 * Now Waypoint specific sort
6836 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6838 GList **list = (GList**)udata;
6839 // Sort named list alphabetically
6840 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6844 * Track specific sort
6846 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6848 GList **list = (GList**)udata;
6849 // Sort named list alphabetically
6850 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6855 gboolean has_same_track_name;
6856 const gchar *same_track_name;
6857 } same_track_name_udata;
6859 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6861 const gchar* namea = (const gchar*) aa;
6862 const gchar* nameb = (const gchar*) bb;
6865 gint result = strcmp ( namea, nameb );
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;
6874 // Leave ordering the same
6879 * Find out if any tracks have the same name in this hash table
6881 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6883 // Sort items by name, then compare if any next to each other are the same
6885 GList *track_names = NULL;
6886 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6889 if ( ! track_names )
6892 same_track_name_udata udata;
6893 udata.has_same_track_name = FALSE;
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...
6902 return udata.has_same_track_name;
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
6910 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
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
6918 same_track_name_udata udata;
6920 GList *track_names = NULL;
6921 udata.has_same_track_name = FALSE;
6922 udata.same_track_name = NULL;
6924 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6927 if ( ! track_names )
6930 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6932 // Still no tracks...
6933 if ( ! dummy_list1 )
6936 while ( udata.has_same_track_name ) {
6938 // Find a track with the same name
6941 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6943 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
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") );
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 );
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 );
6964 if ( trkf && udataU.uuid ) {
6968 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6970 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6973 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6975 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6977 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6981 // Start trying to find same names again...
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 );
6987 // No tracks any more - give up searching
6988 if ( ! dummy_list2 )
6989 udata.has_same_track_name = FALSE;
6993 vik_layers_panel_emit_update ( vlp );
6996 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6998 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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;
7006 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7007 iter = &(vtl->routes_iter);
7008 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
7010 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7011 iter = &(vtl->waypoints_iter);
7012 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
7016 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
7019 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
7021 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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;
7029 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7030 iter = &(vtl->routes_iter);
7031 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
7033 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7034 iter = &(vtl->waypoints_iter);
7035 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
7039 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
7045 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
7047 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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 );
7060 // Sort list alphabetically for better presentation
7061 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7064 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
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),
7072 _("Delete Selection"),
7073 _("Select tracks to delete"));
7076 // Delete requested tracks
7077 // since specificly requested, IMHO no need for extra confirmation
7078 if ( delete_list ) {
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);
7084 g_list_free(delete_list);
7085 vik_layer_emit_update( VIK_LAYER(vtl) );
7092 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
7094 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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 );
7107 // Sort list alphabetically for better presentation
7108 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7111 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
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),
7119 _("Delete Selection"),
7120 _("Select routes to delete") );
7123 // Delete requested routes
7124 // since specificly requested, IMHO no need for extra confirmation
7125 if ( delete_list ) {
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);
7131 g_list_free(delete_list);
7132 vik_layer_emit_update( VIK_LAYER(vtl) );
7137 gboolean has_same_waypoint_name;
7138 const gchar *same_waypoint_name;
7139 } same_waypoint_name_udata;
7141 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
7143 const gchar* namea = (const gchar*) aa;
7144 const gchar* nameb = (const gchar*) bb;
7147 gint result = strcmp ( namea, nameb );
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;
7156 // Leave ordering the same
7161 * Find out if any waypoints have the same name in this layer
7163 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
7165 // Sort items by name, then compare if any next to each other are the same
7167 GList *waypoint_names = NULL;
7168 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7171 if ( ! waypoint_names )
7174 same_waypoint_name_udata udata;
7175 udata.has_same_waypoint_name = FALSE;
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...
7184 return udata.has_same_waypoint_name;
7188 * Force unqiue waypoint names for this layer
7189 * Note the panel is a required parameter to enable the update of the names displayed
7191 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
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
7199 same_waypoint_name_udata udata;
7201 GList *waypoint_names = NULL;
7202 udata.has_same_waypoint_name = FALSE;
7203 udata.same_waypoint_name = NULL;
7205 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7208 if ( ! waypoint_names )
7211 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7213 // Still no waypoints...
7214 if ( ! dummy_list1 )
7217 while ( udata.has_same_waypoint_name ) {
7219 // Find a waypoint with the same name
7220 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
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") );
7231 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7233 trw_layer_waypoint_rename ( vtl, waypoint, newname );
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 );
7241 // No waypoints any more - give up searching
7242 if ( ! dummy_list2 )
7243 udata.has_same_waypoint_name = FALSE;
7247 vik_layers_panel_emit_update ( vlp );
7253 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7255 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
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]) );
7268 // Sort list alphabetically for better presentation
7269 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7271 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7275 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
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),
7281 _("Delete Selection"),
7282 _("Select waypoints to delete"));
7285 // Delete requested waypoints
7286 // since specificly requested, IMHO no need for extra confirmation
7287 if ( delete_list ) {
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);
7293 g_list_free(delete_list);
7295 trw_layer_calculate_bounds_waypoints ( vtl );
7296 vik_layer_emit_update( VIK_LAYER(vtl) );
7304 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7306 vik_treeview_item_toggle_visible ( vt, it );
7312 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7314 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7320 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7322 wp->visible = GPOINTER_TO_INT (on_off);
7328 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7330 wp->visible = !wp->visible;
7336 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
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] );
7343 vik_layer_emit_update ( VIK_LAYER(vtl) );
7349 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
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] );
7356 vik_layer_emit_update ( VIK_LAYER(vtl) );
7362 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
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 );
7368 vik_layer_emit_update ( VIK_LAYER(vtl) );
7374 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7376 trk->visible = GPOINTER_TO_INT (on_off);
7382 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7384 trk->visible = !trk->visible;
7390 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
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] );
7397 vik_layer_emit_update ( VIK_LAYER(vtl) );
7403 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
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] );
7410 vik_layer_emit_update ( VIK_LAYER(vtl) );
7416 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
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 );
7422 vik_layer_emit_update ( VIK_LAYER(vtl) );
7428 static void trw_layer_routes_visibility_off ( menu_array_layer values )
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] );
7435 vik_layer_emit_update ( VIK_LAYER(vtl) );
7441 static void trw_layer_routes_visibility_on ( menu_array_layer values )
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] );
7448 vik_layer_emit_update ( VIK_LAYER(vtl) );
7454 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
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 );
7460 vik_layer_emit_update ( VIK_LAYER(vtl) );
7464 * vik_trw_layer_build_waypoint_list_t:
7466 * Helper function to construct a list of #vik_trw_waypoint_list_t
7468 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
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);
7476 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7477 waypoints = g_list_next ( waypoints );
7479 return waypoints_and_layers;
7483 * trw_layer_create_waypoint_list:
7485 * Create the latest list of waypoints with the associated layer(s)
7486 * Although this will always be from a single layer here
7488 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7490 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7491 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7493 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7497 * trw_layer_analyse_close:
7499 * Stuff to do on dialog closure
7501 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7503 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7504 gtk_widget_destroy ( dialog );
7505 vtl->tracks_analysis_dialog = NULL;
7509 * vik_trw_layer_build_track_list_t:
7511 * Helper function to construct a list of #vik_trw_track_list_t
7513 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7515 GList *tracks_and_layers = NULL;
7516 // build tracks_and_layers list
7518 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7519 vtdl->trk = VIK_TRACK(tracks->data);
7521 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7522 tracks = g_list_next ( tracks );
7524 return tracks_and_layers;
7528 * trw_layer_create_track_list:
7530 * Create the latest list of tracks with the associated layer(s)
7531 * Although this will always be from a single layer here
7533 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
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) );
7540 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7542 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7545 static void trw_layer_tracks_stats ( menu_array_layer values )
7547 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7548 // There can only be one!
7549 if ( vtl->tracks_analysis_dialog )
7552 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7553 VIK_LAYER(vtl)->name,
7555 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7556 trw_layer_create_track_list,
7557 trw_layer_analyse_close );
7563 static void trw_layer_routes_stats ( menu_array_layer values )
7565 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7566 // There can only be one!
7567 if ( vtl->tracks_analysis_dialog )
7570 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7571 VIK_LAYER(vtl)->name,
7573 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7574 trw_layer_create_track_list,
7575 trw_layer_analyse_close );
7578 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7580 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7581 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7583 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7586 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7588 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7589 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
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);
7597 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7599 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7600 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
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);
7612 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7614 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7616 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7618 // No actual change to the name supplied
7620 if (strcmp(newname, wp->name) == 0 )
7623 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
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?"),
7633 // Update WP name and refresh the treeview
7634 vik_waypoint_set_name (wp, newname);
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 );
7639 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7644 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7646 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7648 // No actual change to the name supplied
7650 if (strcmp(newname, trk->name) == 0)
7653 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
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?"),
7662 // Update track name and refresh GUI parts
7663 vik_track_set_name (trk, newname);
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 );
7670 // Property Dialog of the track
7671 vik_trw_layer_propwin_update ( trk );
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 );
7676 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7681 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7683 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7685 // No actual change to the name supplied
7687 if (strcmp(newname, trk->name) == 0)
7690 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
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?"),
7699 // Update track name and refresh GUI parts
7700 vik_track_set_name (trk, newname);
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 );
7707 // Property Dialog of the track
7708 vik_trw_layer_propwin_update ( trk );
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 );
7713 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7720 static gboolean is_valid_geocache_name ( gchar *str )
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]));
7726 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
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 );
7732 #ifdef VIK_CONFIG_GOOGLE
7733 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
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) );
7739 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7741 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
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);
7752 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7753 /* viewpoint is now available instead */
7754 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7756 static menu_array_sublayer pass_along;
7758 gboolean rv = FALSE;
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
7769 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
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 );
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 );
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 );
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 );
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 );
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 );
7804 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
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 );
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 );
7820 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
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 );
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 );
7838 if ( wp && wp->image )
7840 // Set up image paramater
7841 pass_along[MA_MISC] = wp->image;
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 );
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 );
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 );
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 );
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 );
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 );
7893 gtk_widget_set_sensitive ( item, FALSE );
7896 item = gtk_menu_item_new ();
7897 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7898 gtk_widget_show ( item );
7901 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
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 );
7911 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
7967 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
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 );
7977 item = gtk_menu_item_new ();
7978 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7979 gtk_widget_show ( item );
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 );
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) );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
8043 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
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 );
8054 item = gtk_menu_item_new ();
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8056 gtk_widget_show ( item );
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 );
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) );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
8113 gtk_widget_show ( item );
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 );
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 );
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 );
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 );
8143 GtkWidget *upload_submenu = gtk_menu_new ();
8145 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8147 item = gtk_menu_item_new ();
8148 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8149 gtk_widget_show ( item );
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 );
8161 item = gtk_menu_item_new ();
8162 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8163 gtk_widget_show ( item );
8166 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8167 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
8253 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8254 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
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 );
8261 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8262 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
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 );
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 );
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 );
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 );
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 );
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) );
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 );
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) );
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) );
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 );
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) );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
8396 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8397 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
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 );
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 );
8414 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8415 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
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 );
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 );
8431 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8433 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8434 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
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 );
8443 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8444 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
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 );
8452 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8453 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
8512 #ifdef VIK_CONFIG_GOOGLE
8513 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
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 );
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 );
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 );
8541 /* ATM This function is only available via the layers panel, due to needing a 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 ) );
8547 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8548 gtk_widget_show ( item );
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 );
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 ) {
8564 item = gtk_menu_item_new ();
8565 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8566 gtk_widget_show ( item );
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 );
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 );
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 );
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 );
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 );
8605 gtk_widget_show_all ( GTK_WIDGET(menu) );
8610 // TODO: Probably better to rework this track manipulation in viktrack.c
8611 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8614 if (!vtl->current_tpl)
8617 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8618 VikTrackpoint *tp_other = NULL;
8621 if (!vtl->current_tpl->prev)
8623 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8625 if (!vtl->current_tpl->next)
8627 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8630 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
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 );
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 );
8642 /* Now other properties that can be interpolated */
8643 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
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;
8652 if (tp_current->speed != NAN && tp_other->speed != NAN)
8653 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
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;
8661 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
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 );
8666 // Otherwise try routes
8667 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8671 gint index = g_list_index ( trk->trackpoints, tp_current );
8675 // NB no recalculation of bounds since it is inserted between points
8676 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8681 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8687 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8691 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8693 if ( vtl->current_tpl )
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));
8702 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8704 VikTrack *trk = vtl->current_tp_track;
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, ¢er );
8709 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8712 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8714 g_assert ( vtl->tpwin != NULL );
8715 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8716 trw_layer_cancel_current_tp ( vtl, TRUE );
8718 if ( vtl->current_tpl == NULL )
8721 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
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 );
8726 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8728 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8730 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8734 trw_layer_trackpoint_selected_delete ( vtl, tr );
8736 if ( vtl->current_tpl )
8737 // Reset dialog with the available adjacent trackpoint
8738 my_tpwin_set_tp ( vtl );
8740 vik_layer_emit_update(VIK_LAYER(vtl));
8742 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8744 if ( vtl->current_tp_track ) {
8745 vtl->current_tpl = vtl->current_tpl->next;
8746 my_tpwin_set_tp ( vtl );
8748 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8750 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8752 if ( vtl->current_tp_track ) {
8753 vtl->current_tpl = vtl->current_tpl->prev;
8754 my_tpwin_set_tp ( vtl );
8756 vik_layer_emit_update(VIK_LAYER(vtl));
8758 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8760 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8761 vik_layer_emit_update(VIK_LAYER(vtl));
8763 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8764 vik_layer_emit_update(VIK_LAYER(vtl));
8768 * trw_layer_dialog_shift:
8769 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8771 * Try to reposition a dialog if it's over the specified coord
8772 * so to not obscure the item of interest
8774 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8776 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
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 ();
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 );
8786 gint win_size_x, win_size_y;
8787 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
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 );
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 );
8797 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8798 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8800 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8802 gint vp_xx, vp_yy; // In viewport pixels
8803 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8805 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8809 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
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;
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)) ) {
8821 gint hh = vik_viewport_get_height ( vvp );
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;
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 );
8833 // Point in top half, move dialog down
8834 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8838 // Shift left<->right
8839 gint ww = vik_viewport_get_width ( vvp );
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;
8846 if ( vp_xx > ww/2 ) {
8847 // Point on right, move window to left
8848 gtk_window_move ( dialog, offset_x, dia_pos_y );
8851 // Point on left, move right
8852 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8860 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
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 );
8869 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8871 if ( vtl->current_tpl ) {
8872 // get tp pixel position
8873 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8875 // Shift up<->down to try not to obscure the trackpoint.
8876 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8880 if ( vtl->current_tpl )
8881 if ( vtl->current_tp_track )
8882 my_tpwin_set_tp ( vtl );
8883 /* set layer name and TP data */
8886 /***************************************************************************
8888 ***************************************************************************/
8890 /*** Utility data structures and functions ****/
8894 gint closest_x, closest_y;
8895 gboolean draw_images;
8896 gpointer *closest_wp_id;
8897 VikWaypoint *closest_wp;
8903 gint closest_x, closest_y;
8904 gpointer closest_track_id;
8905 VikTrackpoint *closest_tp;
8911 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8917 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
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;
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;
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)))
8937 params->closest_wp_id = id;
8938 params->closest_wp = wp;
8939 params->closest_x = x;
8940 params->closest_y = y;
8944 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8946 GList *tpl = t->trackpoints;
8952 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8958 tp = VIK_TRACKPOINT(tpl->data);
8960 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
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)))
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;
8976 // ATM: Leave this as 'Track' only.
8977 // Not overly bothered about having a snap to route trackpoint capability
8978 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8980 TPSearchParams params;
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, ¶ms);
8988 return params.closest_tp;
8991 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8993 WPSearchParams params;
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, ¶ms);
9001 return params.closest_wp;
9005 // Some forward declarations
9006 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
9007 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
9008 static void marker_end_move ( tool_ed_t *t );
9011 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
9015 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
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!
9022 if ( event->state & GDK_CONTROL_MASK )
9024 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9026 new_coord = tp->coord;
9030 if ( event->state & GDK_SHIFT_MASK )
9032 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9034 new_coord = wp->coord;
9038 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9040 marker_moveto ( t, x, y );
9047 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
9049 if ( t->holding && event->button == 1 )
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)
9057 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9060 if ( event->state & GDK_CONTROL_MASK )
9062 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9064 new_coord = tp->coord;
9068 if ( event->state & GDK_SHIFT_MASK )
9070 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9072 new_coord = wp->coord;
9075 marker_end_move ( t );
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;
9087 if ( vtl->current_tpl ) {
9088 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9090 if ( vtl->current_tp_track )
9091 vik_track_calculate_bounds ( vtl->current_tp_track );
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
9100 vik_layer_emit_update ( VIK_LAYER(vtl) );
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
9111 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
9113 if ( event->button != 1 )
9116 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9119 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9123 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
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
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;
9136 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
9138 if ( wp_params.closest_wp ) {
9141 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
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;
9152 marker_begin_move (tet, event->x, event->y);
9155 vtl->current_wp = wp_params.closest_wp;
9156 vtl->current_wp_id = wp_params.closest_wp_id;
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 );
9167 vik_layer_emit_update ( VIK_LAYER(vtl) );
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;
9183 if (vtl->tracks_visible) {
9184 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9186 if ( tp_params.closest_tp ) {
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 );
9191 tet->is_waypoint = FALSE;
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);
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 );
9207 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9210 my_tpwin_set_tp ( vtl );
9212 vik_layer_emit_update ( VIK_LAYER(vtl) );
9217 // Try again for routes
9218 if (vtl->routes_visible) {
9219 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9221 if ( tp_params.closest_tp ) {
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 );
9226 tet->is_waypoint = FALSE;
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);
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 );
9242 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9245 my_tpwin_set_tp ( vtl );
9247 vik_layer_emit_update ( VIK_LAYER(vtl) );
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 );
9258 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9263 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9265 if ( event->button != 3 )
9268 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9271 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9274 /* Post menu for the currently selected item */
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 ) {
9280 if ( track->name ) {
9282 if ( vtl->track_right_click_menu )
9283 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9285 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9292 if ( track->is_route )
9293 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9295 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9297 if ( trkf && udataU.uuid ) {
9300 if ( track->is_route )
9301 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9303 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9305 trw_layer_sublayer_add_menu_items ( vtl,
9306 vtl->track_right_click_menu,
9308 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9314 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
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 ) {
9325 if ( vtl->wp_right_click_menu )
9326 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9328 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9331 udata.wp = waypoint;
9334 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9336 if ( wpf && udata.uuid ) {
9337 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9339 trw_layer_sublayer_add_menu_items ( vtl,
9340 vtl->wp_right_click_menu,
9342 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9347 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9356 /* background drawing hook, to be passed the viewport */
9357 static gboolean tool_sync_done = TRUE;
9359 static gboolean tool_sync(gpointer data)
9361 VikViewport *vvp = data;
9362 gdk_threads_enter();
9363 vik_viewport_sync(vvp);
9364 tool_sync_done = TRUE;
9365 gdk_threads_leave();
9369 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
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);
9381 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
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 );
9390 if (tool_sync_done) {
9391 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9392 tool_sync_done = FALSE;
9396 static void marker_end_move ( tool_ed_t *t )
9398 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9399 g_object_unref ( t->gc );
9404 /*** Edit waypoint ****/
9406 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9408 tool_ed_t *t = g_new(tool_ed_t, 1);
9414 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9419 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9421 WPSearchParams params;
9422 tool_ed_t *t = data;
9423 VikViewport *vvp = t->vvp;
9425 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9432 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9435 if ( vtl->current_wp && vtl->current_wp->visible )
9437 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9439 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9441 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9442 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9444 if ( event->button == 3 )
9445 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9447 marker_begin_move(t, event->x, event->y);
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, ¶ms);
9460 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9462 if ( event->button == 3 )
9463 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9465 marker_begin_move(t, event->x, event->y);
9468 else if ( params.closest_wp )
9470 if ( event->button == 3 )
9471 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9473 vtl->waypoint_rightclick = FALSE;
9475 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9477 vtl->current_wp = params.closest_wp;
9478 vtl->current_wp_id = params.closest_wp_id;
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) );
9485 vtl->current_wp = NULL;
9486 vtl->current_wp_id = NULL;
9487 vtl->waypoint_rightclick = FALSE;
9488 vik_layer_emit_update ( VIK_LAYER(vtl) );
9492 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9494 tool_ed_t *t = data;
9495 VikViewport *vvp = t->vvp;
9497 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9502 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9505 if ( event->state & GDK_CONTROL_MASK )
9507 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9509 new_coord = tp->coord;
9513 if ( event->state & GDK_SHIFT_MASK )
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;
9522 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9524 marker_moveto ( t, x, y );
9531 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9533 tool_ed_t *t = data;
9534 VikViewport *vvp = t->vvp;
9536 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9539 if ( t->holding && event->button == 1 )
9542 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9545 if ( event->state & GDK_CONTROL_MASK )
9547 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9549 new_coord = tp->coord;
9553 if ( event->state & GDK_SHIFT_MASK )
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;
9560 marker_end_move ( t );
9562 vtl->current_wp->coord = new_coord;
9564 trw_layer_calculate_bounds_waypoints ( vtl );
9565 vik_layer_emit_update ( VIK_LAYER(vtl) );
9568 /* PUT IN RIGHT PLACE!!! */
9569 if ( event->button == 3 && vtl->waypoint_rightclick )
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() );
9578 vtl->waypoint_rightclick = FALSE;
9583 /*** New track ****/
9585 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9592 GdkDrawable *drawable;
9598 * Draw specified pixmap
9600 static gboolean draw_sync ( gpointer data )
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,
9611 0, 0, 0, 0, -1, -1);
9612 ds->vtl->draw_sync_done = TRUE;
9613 gdk_threads_leave();
9619 static gchar* distance_string (gdouble distance)
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));
9632 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
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));
9641 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
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);
9651 g_sprintf(str, "%d km", (int)distance/1000);
9655 return g_strdup (str);
9659 * Actually set the message in statusbar
9661 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
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);
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);
9674 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
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);
9683 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
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 );
9689 g_free ( str_total );
9693 * Figure out what information should be set in the statusbar and then write it
9695 static void update_statusbar ( VikTrwLayer *vtl )
9697 // Get elevation data
9698 gdouble elev_gain, elev_loss;
9699 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9701 /* Find out actual distance of current track */
9702 gdouble distance = vik_track_get_length (vtl->current_track);
9704 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9708 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
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);
9714 static GdkPixmap *pixmap = NULL;
9716 // Need to check in case window has been resized
9717 w1 = vik_viewport_get_width(vvp);
9718 h1 = vik_viewport_get_height(vvp);
9720 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
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 );
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);
9734 draw_sync_t *passalong;
9737 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
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
9747 /* Find out actual distance of current track */
9748 gdouble distance = vik_track_get_length (vtl->current_track);
9750 // Now add distance to where the pointer is //
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;
9758 // Get elevation data
9759 gdouble elev_gain, elev_loss;
9760 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9762 // Adjust elevation data (if available) for the current pointer position
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 )
9770 elev_gain += elev_new - last_tpt->altitude;
9773 elev_loss += last_tpt->altitude - elev_new;
9778 // Display of the distance 'tooltip' during track creation is controlled by a preference
9780 if ( a_vik_get_create_track_tooltip() ) {
9782 gchar *str = distance_string (distance);
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);
9788 pango_layout_get_pixel_size ( pl, &wd, &hd );
9791 // offset from cursor a bit depending on font size
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);
9800 g_object_unref ( G_OBJECT ( pl ) );
9801 g_object_unref ( G_OBJECT ( background_block_gc ) );
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;
9813 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9815 // Update statusbar with full gain/loss information
9816 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
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;
9823 return VIK_LAYER_TOOL_ACK;
9826 // NB vtl->current_track must be valid
9827 static void undo_trackpoint_add ( VikTrwLayer *vtl )
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 );
9837 vik_track_calculate_bounds ( vtl->current_track );
9841 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
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 );
9849 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9851 vtl->current_track = NULL;
9852 vik_layer_emit_update ( VIK_LAYER(vtl) );
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) );
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
9869 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9873 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
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;
9883 if ( event->button == 3 )
9885 if ( !vtl->current_track )
9887 undo_trackpoint_add ( vtl );
9888 update_statusbar ( vtl );
9889 vik_layer_emit_update ( VIK_LAYER(vtl) );
9893 if ( event->type == GDK_2BUTTON_PRESS )
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 )
9898 /* undo last, then end */
9899 undo_trackpoint_add ( vtl );
9900 vtl->current_track = NULL;
9902 vik_layer_emit_update ( VIK_LAYER(vtl) );
9906 tp = vik_trackpoint_new();
9907 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9909 /* snap to other TP */
9910 if ( event->state & GDK_CONTROL_MASK )
9912 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9914 tp->coord = other_tp->coord;
9917 tp->newsegment = FALSE;
9918 tp->has_timestamp = FALSE;
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 );
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;
9932 vik_layer_emit_update ( VIK_LAYER(vtl) );
9936 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9938 // if we were running the route finder, cancel it
9939 vtl->route_finder_started = FALSE;
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 ) ))
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 );
9950 new_track_create_common ( vtl, name );
9953 return tool_new_track_or_route_click ( vtl, event, vvp );
9956 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
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;
9965 /*** New route ****/
9967 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9972 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9974 // if we were running the route finder, cancel it
9975 vtl->route_finder_started = FALSE;
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 ) ) )
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 );
9987 new_route_create_common ( vtl, name );
9990 return tool_new_track_or_route_click ( vtl, event, vvp );
9993 /*** New waypoint ****/
9995 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
10000 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10003 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
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) );
10014 /*** Edit trackpoint ****/
10016 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
10018 tool_ed_t *t = g_new(tool_ed_t, 1);
10020 t->holding = FALSE;
10024 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
10029 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
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
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) );
10049 if ( event->button != 1 )
10052 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10055 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
10058 if ( vtl->current_tpl )
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));
10067 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
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 );
10078 if ( vtl->tracks_visible )
10079 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
10081 if ( params.closest_tp )
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) );
10093 if ( vtl->routes_visible )
10094 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
10096 if ( params.closest_tp )
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) );
10108 /* these aren't the droids you're looking for */
10112 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
10114 tool_ed_t *t = data;
10115 VikViewport *vvp = t->vvp;
10117 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10122 VikCoord new_coord;
10123 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10126 if ( event->state & GDK_CONTROL_MASK )
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;
10132 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10135 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
10136 marker_moveto ( t, x, y );
10144 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10146 tool_ed_t *t = data;
10147 VikViewport *vvp = t->vvp;
10149 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10151 if ( event->button != 1)
10154 if ( t->holding ) {
10155 VikCoord new_coord;
10156 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10159 if ( event->state & GDK_CONTROL_MASK )
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;
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 );
10170 marker_end_move ( t );
10172 /* diff dist is diff from orig */
10174 my_tpwin_set_tp ( vtl );
10176 vik_layer_emit_update ( VIK_LAYER(vtl) );
10183 /*** Extended Route Finder ***/
10185 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10190 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10193 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10195 g_free ( new_end );
10196 vik_layer_emit_update ( VIK_LAYER(vtl) );
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 );
10211 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
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 );
10219 else if ( event->button == 2 ) {
10220 vtl->draw_sync_do = FALSE;
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 );
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;
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 );
10235 vtl->route_finder_started = TRUE;
10236 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
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 ( );
10242 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
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 );
10250 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10253 /* Give GTK a change to display the new status bar before querying the web */
10254 while ( gtk_events_pending ( ) )
10255 gtk_main_iteration ( );
10257 gboolean find_status = vik_routing_default_find ( vtl, start, end );
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 );
10269 vik_layer_emit_update ( VIK_LAYER(vtl) );
10271 vtl->current_track = NULL;
10273 // create a new route where we will add the planned route to
10274 gboolean ret = tool_new_route_click( vtl, event, vvp );
10276 vtl->route_finder_started = TRUE;
10283 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
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) );
10290 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10291 tool_extended_route_finder_undo ( vtl );
10298 /*** Show picture ****/
10300 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10305 /* Params are: vvp, event, last match found or NULL */
10306 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10308 if ( wp->image && wp->visible )
10310 gint x, y, slackx, slacky;
10311 GdkEventButton *event = (GdkEventButton *) params[1];
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 )
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. */
10326 static void trw_layer_show_picture ( menu_array_sublayer values )
10328 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
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 ) )
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 );
10342 #endif /* WINDOWS */
10345 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10347 gpointer params[3] = { vvp, event, NULL };
10348 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10350 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
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 */
10360 return FALSE; /* go through other layers, searching for a match */
10363 /***************************************************************************
10365 ***************************************************************************/
10368 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10370 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10371 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10374 /* Structure for thumbnail creating data used in the background thread */
10376 VikTrwLayer *vtl; // Layer needed for redrawing
10377 GSList *pics; // Image list
10378 } thumbnail_create_thread_data;
10380 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10382 guint total = g_slist_length(tctd->pics), done = 0;
10383 while ( tctd->pics )
10385 a_thumbnails_create ( (gchar *) tctd->pics->data );
10386 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10388 return -1; /* Abort thread */
10390 tctd->pics = tctd->pics->next;
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
10400 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10402 while ( tctd->pics )
10404 g_free ( tctd->pics->data );
10405 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10410 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10412 if ( ! vtl->has_verified_thumbnails )
10414 GSList *pics = NULL;
10415 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
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) );
10423 a_background_thread ( BACKGROUND_POOL_LOCAL,
10424 VIK_GTK_WINDOW_FROM_LAYER(vtl),
10426 (vik_thr_func) create_thumbnails_thread,
10428 (vik_thr_free_func) thumbnail_create_thread_free,
10436 static const gchar* my_track_colors ( gint ii )
10438 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10450 // Fast and reliable way of returning a colour
10451 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10454 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10456 GHashTableIter iter;
10457 gpointer key, value;
10461 g_hash_table_iter_init ( &iter, vtl->tracks );
10463 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10465 // Tracks get a random spread of colours if not already assigned
10466 if ( ! VIK_TRACK(value)->has_color ) {
10467 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10468 VIK_TRACK(value)->color = vtl->track_color;
10470 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10472 VIK_TRACK(value)->has_color = TRUE;
10475 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10478 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10484 g_hash_table_iter_init ( &iter, vtl->routes );
10486 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10488 // Routes get an intermix of reds
10489 if ( ! VIK_TRACK(value)->has_color ) {
10491 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10493 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10494 VIK_TRACK(value)->has_color = TRUE;
10497 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10504 * (Re)Calculate the bounds of the waypoints in this layer,
10505 * This should be called whenever waypoints are changed
10507 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10509 struct LatLon topleft = { 0.0, 0.0 };
10510 struct LatLon bottomright = { 0.0, 0.0 };
10513 GHashTableIter iter;
10514 gpointer key, value;
10516 g_hash_table_iter_init ( &iter, vtl->waypoints );
10518 // Set bounds to first point
10519 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10520 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10521 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10524 // Ensure there is another point...
10525 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10527 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10529 // See if this point increases the bounds.
10530 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10532 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10533 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10534 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10535 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10539 vtl->waypoints_bbox.north = topleft.lat;
10540 vtl->waypoints_bbox.east = bottomright.lon;
10541 vtl->waypoints_bbox.south = bottomright.lat;
10542 vtl->waypoints_bbox.west = topleft.lon;
10545 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10547 vik_track_calculate_bounds ( trk );
10550 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10552 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10553 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10556 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10558 if ( ! VIK_LAYER(vtl)->vt )
10561 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10562 if ( g_hash_table_size (vtl->tracks) > 1 )
10563 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10565 if ( g_hash_table_size (vtl->routes) > 1 )
10566 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10568 if ( g_hash_table_size (vtl->waypoints) > 1 )
10569 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10572 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10574 if ( VIK_LAYER(vtl)->realized )
10575 trw_layer_verify_thumbnails ( vtl, vvp );
10576 trw_layer_track_alloc_colors ( vtl );
10578 trw_layer_calculate_bounds_waypoints ( vtl );
10579 trw_layer_calculate_bounds_tracks ( vtl );
10581 // Apply treeview sort after loading all the tracks for this layer
10582 // (rather than sorted insert on each individual track additional)
10583 // and after subsequent changes to the properties as the specified order may have changed.
10584 // since the sorting of a treeview section is now very quick
10585 // NB sorting is also performed after every name change as well to maintain the list order
10586 trw_layer_sort_all ( vtl );
10588 // Setting metadata time if not otherwise set
10589 if ( vtl->metadata ) {
10591 gboolean need_to_set_time = TRUE;
10592 if ( vtl->metadata->timestamp ) {
10593 need_to_set_time = FALSE;
10594 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10595 need_to_set_time = TRUE;
10598 if ( need_to_set_time ) {
10599 // Could rewrite this as a general get first time of a TRW Layer function
10600 GTimeVal timestamp;
10601 timestamp.tv_usec = 0;
10602 gboolean has_timestamp = FALSE;
10605 gl = g_hash_table_get_values ( vtl->tracks );
10606 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10607 gl = g_list_first ( gl );
10609 // Check times of tracks
10611 // Only need to check the first track as they have been sorted by time
10612 VikTrack *trk = (VikTrack*)gl->data;
10613 // Assume trackpoints already sorted by time
10614 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10615 if ( tpt && tpt->has_timestamp ) {
10616 timestamp.tv_sec = tpt->timestamp;
10617 has_timestamp = TRUE;
10619 g_list_free ( gl );
10622 if ( !has_timestamp ) {
10623 // 'Last' resort - current time
10624 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10625 g_get_current_time ( ×tamp );
10627 // Check times of waypoints
10628 gl = g_hash_table_get_values ( vtl->waypoints );
10630 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10631 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10632 if ( wpt->has_timestamp ) {
10633 if ( timestamp.tv_sec > wpt->timestamp ) {
10634 timestamp.tv_sec = wpt->timestamp;
10635 has_timestamp = TRUE;
10639 g_list_free ( gl );
10642 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10647 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10649 return vtl->coord_mode;
10653 * Uniquify the whole layer
10654 * Also requires the layers panel as the names shown there need updating too
10655 * Returns whether the operation was successful or not
10657 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10659 if ( vtl && vlp ) {
10660 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10661 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10662 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10668 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10670 vik_coord_convert ( &(wp->coord), *dest_mode );
10673 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10675 vik_track_convert ( tr, *dest_mode );
10678 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10680 if ( vtl->coord_mode != dest_mode )
10682 vtl->coord_mode = dest_mode;
10683 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10684 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10685 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10689 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10691 vtl->menu_selection = selection;
10694 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10696 return (vtl->menu_selection);
10699 /* ----------- Downloading maps along tracks --------------- */
10701 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10703 /* TODO: calculating based on current size of viewport */
10704 const gdouble w_at_zoom_0_125 = 0.0013;
10705 const gdouble h_at_zoom_0_125 = 0.0011;
10706 gdouble zoom_factor = zoom_level/0.125;
10708 wh->lat = h_at_zoom_0_125 * zoom_factor;
10709 wh->lon = w_at_zoom_0_125 * zoom_factor;
10711 return 0; /* all OK */
10714 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10716 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10717 (dist->lat >= ABS(to->north_south - from->north_south)))
10720 VikCoord *coord = g_malloc(sizeof(VikCoord));
10721 coord->mode = VIK_COORD_LATLON;
10723 if (ABS(gradient) < 1) {
10724 if (from->east_west > to->east_west)
10725 coord->east_west = from->east_west - dist->lon;
10727 coord->east_west = from->east_west + dist->lon;
10728 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10730 if (from->north_south > to->north_south)
10731 coord->north_south = from->north_south - dist->lat;
10733 coord->north_south = from->north_south + dist->lat;
10734 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10740 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10742 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10743 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10745 VikCoord *next = from;
10747 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10749 list = g_list_prepend(list, next);
10755 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10757 typedef struct _Rect {
10762 #define GLRECT(iter) ((Rect *)((iter)->data))
10765 GList *rects_to_download = NULL;
10768 if (get_download_area_width(vvp, zoom_level, &wh))
10771 GList *iter = tr->trackpoints;
10775 gboolean new_map = TRUE;
10776 VikCoord *cur_coord, tl, br;
10779 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10781 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10782 rect = g_malloc(sizeof(Rect));
10785 rect->center = *cur_coord;
10786 rects_to_download = g_list_prepend(rects_to_download, rect);
10791 gboolean found = FALSE;
10792 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10793 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10804 GList *fillins = NULL;
10805 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10806 /* seems that ATM the function get_next_coord works only for LATLON */
10807 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10808 /* fill-ins for far apart points */
10809 GList *cur_rect, *next_rect;
10810 for (cur_rect = rects_to_download;
10811 (next_rect = cur_rect->next) != NULL;
10812 cur_rect = cur_rect->next) {
10813 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10814 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10815 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10819 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10822 GList *fiter = fillins;
10824 cur_coord = (VikCoord *)(fiter->data);
10825 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10826 rect = g_malloc(sizeof(Rect));
10829 rect->center = *cur_coord;
10830 rects_to_download = g_list_prepend(rects_to_download, rect);
10831 fiter = fiter->next;
10835 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10836 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10840 for (iter = fillins; iter; iter = iter->next)
10841 g_free(iter->data);
10842 g_list_free(fillins);
10844 if (rects_to_download) {
10845 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10846 g_free(rect_iter->data);
10847 g_list_free(rects_to_download);
10851 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10855 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10856 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10857 gint selected_zoom, default_zoom;
10859 VikTrwLayer *vtl = values[MA_VTL];
10860 VikLayersPanel *vlp = values[MA_VLP];
10862 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10863 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10865 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10869 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10871 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10872 int num_maps = g_list_length(vmls);
10875 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10879 // Convert from list of vmls to list of names. Allowing the user to select one of them
10880 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10881 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10883 gchar **np = map_names;
10884 VikMapsLayer **lp = map_layers;
10886 for (i = 0; i < num_maps; i++) {
10887 vml = (VikMapsLayer *)(vmls->data);
10889 *np++ = vik_maps_layer_get_map_label(vml);
10892 // Mark end of the array lists
10896 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10897 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10898 if (cur_zoom == zoom_vals[default_zoom])
10901 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10903 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10906 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10909 for (i = 0; i < num_maps; i++)
10910 g_free(map_names[i]);
10912 g_free(map_layers);
10918 /**** lowest waypoint number calculation ***/
10919 static gint highest_wp_number_name_to_number(const gchar *name) {
10920 if ( strlen(name) == 3 ) {
10921 int n = atoi(name);
10922 if ( n < 100 && name[0] != '0' )
10924 if ( n < 10 && name[0] != '0' )
10932 static void highest_wp_number_reset(VikTrwLayer *vtl)
10934 vtl->highest_wp_number = -1;
10937 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10939 /* if is bigger that top, add it */
10940 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10941 if ( new_wp_num > vtl->highest_wp_number )
10942 vtl->highest_wp_number = new_wp_num;
10945 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10947 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10948 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10949 if ( vtl->highest_wp_number == old_wp_num ) {
10951 vtl->highest_wp_number--;
10953 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10954 /* search down until we find something that *does* exist */
10956 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10957 vtl->highest_wp_number--;
10958 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10963 /* get lowest unused number */
10964 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10967 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10969 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10970 return g_strdup(buf);
10974 * trw_layer_create_track_list_both:
10976 * Create the latest list of tracks and routes
10978 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10980 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10981 GList *tracks = NULL;
10982 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10983 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10985 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10988 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10990 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10992 gchar *title = NULL;
10993 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10994 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10996 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10998 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
11002 static void trw_layer_track_list_dialog ( menu_array_layer values )
11004 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11006 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
11007 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
11011 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
11013 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11015 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
11016 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );