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 gboolean have_geojson_export = FALSE;
783 // NB Only performed once per program run
784 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
786 if ( g_find_program_in_path( "rednotebook" ) ) {
787 gchar *mystdout = NULL;
788 gchar *mystderr = NULL;
789 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
790 if ( g_spawn_command_line_sync ( "rednotebook --version", &mystdout, &mystderr, NULL, NULL ) ) {
791 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
793 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
795 g_warning ("Diary: stderr: %s", mystderr );
797 gchar **tokens = NULL;
798 if ( stdout && g_strcmp0(mystdout, "") )
799 tokens = g_strsplit(mystdout, " ", 0);
801 tokens = g_strsplit(mystderr, " ", 0);
804 gchar *token = tokens[num];
805 while ( token && num < 2 ) {
807 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
808 have_diary_program = TRUE;
813 g_strfreev ( tokens );
819 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
820 have_geojson_export = TRUE;
824 GType vik_trw_layer_get_type ()
826 static GType vtl_type = 0;
830 static const GTypeInfo vtl_info =
832 sizeof (VikTrwLayerClass),
833 NULL, /* base_init */
834 NULL, /* base_finalize */
835 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
836 NULL, /* class_finalize */
837 NULL, /* class_data */
838 sizeof (VikTrwLayer),
840 NULL /* instance init */
842 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
847 VikTRWMetadata *vik_trw_metadata_new()
849 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
852 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
857 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
859 return vtl->metadata;
862 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
865 vik_trw_metadata_free ( vtl->metadata );
866 vtl->metadata = metadata;
871 const gchar *date_str;
873 const VikWaypoint *wpt;
878 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
882 // Might be an easier way to compare dates rather than converting the strings all the time...
883 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
884 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
886 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
895 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
899 // Might be an easier way to compare dates rather than converting the strings all the time...
900 if ( wpt->has_timestamp ) {
901 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
903 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
913 * Find an item by date
915 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
919 df.date_str = date_str;
924 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
926 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
928 if ( select && df.found ) {
929 if ( do_tracks && df.trk ) {
930 struct LatLon maxmin[2] = { {0,0}, {0,0} };
931 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
932 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
933 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
936 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
937 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
939 vik_layer_emit_update ( VIK_LAYER(vtl) );
944 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
946 static menu_array_sublayer values;
952 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
955 values[MA_VTL] = vtl;
956 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
957 values[MA_SUBLAYER_ID] = sublayer;
958 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
960 trw_layer_delete_item ( values );
963 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
965 static menu_array_sublayer values;
971 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
974 values[MA_VTL] = vtl;
975 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
976 values[MA_SUBLAYER_ID] = sublayer;
977 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
979 trw_layer_copy_item_cb(values);
980 trw_layer_cut_item_cb(values);
983 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
985 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
986 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
987 gpointer * sublayer = values[MA_SUBLAYER_ID];
991 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
995 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
996 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
997 if ( wp && wp->name )
1000 name = NULL; // Broken :(
1002 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1003 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1004 if ( trk && trk->name )
1007 name = NULL; // Broken :(
1010 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1011 if ( trk && trk->name )
1014 name = NULL; // Broken :(
1017 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1018 subtype, len, name, data);
1022 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1024 trw_layer_copy_item_cb(values);
1025 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1026 trw_layer_delete_item(values);
1029 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1031 // Slightly cheating method, routing via the panels capability
1032 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1035 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1045 GByteArray *ba = g_byte_array_new ();
1047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1048 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1049 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1050 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1052 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1055 g_byte_array_append ( ba, id, il );
1063 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1070 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1074 w = vik_waypoint_unmarshall ( item, len );
1075 // When copying - we'll create a new name based on the original
1076 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1077 vik_trw_layer_add_waypoint ( vtl, name, w );
1078 waypoint_convert (NULL, w, &vtl->coord_mode);
1081 trw_layer_calculate_bounds_waypoints ( vtl );
1083 // Consider if redraw necessary for the new item
1084 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1085 vik_layer_emit_update ( VIK_LAYER(vtl) );
1088 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1092 t = vik_track_unmarshall ( item, len );
1093 // When copying - we'll create a new name based on the original
1094 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1095 vik_trw_layer_add_track ( vtl, name, t );
1096 vik_track_convert (t, vtl->coord_mode);
1099 // Consider if redraw necessary for the new item
1100 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1101 vik_layer_emit_update ( VIK_LAYER(vtl) );
1104 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1108 t = vik_track_unmarshall ( item, len );
1109 // When copying - we'll create a new name based on the original
1110 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1111 vik_trw_layer_add_route ( vtl, name, t );
1112 vik_track_convert (t, vtl->coord_mode);
1115 // Consider if redraw necessary for the new item
1116 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1117 vik_layer_emit_update ( VIK_LAYER(vtl) );
1123 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1130 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1134 case PARAM_TV: vtl->tracks_visible = data.b; break;
1135 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1136 case PARAM_RV: vtl->routes_visible = data.b; break;
1137 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1138 case PARAM_TLFONTSIZE:
1139 if ( data.u < FS_NUM_SIZES ) {
1140 vtl->track_font_size = data.u;
1141 g_free ( vtl->track_fsize_str );
1142 switch ( vtl->track_font_size ) {
1143 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1144 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1145 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1146 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1147 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1148 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1149 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1153 case PARAM_DM: vtl->drawmode = data.u; break;
1155 vtl->track_color = data.c;
1156 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1158 case PARAM_DP: vtl->drawpoints = data.b; break;
1160 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1161 vtl->drawpoints_size = data.u;
1163 case PARAM_DE: vtl->drawelevation = data.b; break;
1164 case PARAM_DS: vtl->drawstops = data.b; break;
1165 case PARAM_DL: vtl->drawlines = data.b; break;
1166 case PARAM_DD: vtl->drawdirections = data.b; break;
1168 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1169 vtl->drawdirections_size = data.u;
1171 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1172 vtl->stop_length = data.u;
1174 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1175 vtl->elevation_factor = data.u;
1177 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1179 vtl->line_thickness = data.u;
1180 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1183 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1185 vtl->bg_line_thickness = data.u;
1186 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1190 vtl->track_bg_color = data.c;
1191 if ( vtl->track_bg_gc )
1192 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1194 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1195 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1196 case PARAM_DLA: vtl->drawlabels = data.b; break;
1197 case PARAM_DI: vtl->drawimages = data.b; break;
1198 case PARAM_IS: if ( data.u != vtl->image_size )
1200 vtl->image_size = data.u;
1201 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1202 g_queue_free ( vtl->image_cache );
1203 vtl->image_cache = g_queue_new ();
1206 case PARAM_IA: vtl->image_alpha = data.u; break;
1207 case PARAM_ICS: vtl->image_cache_size = data.u;
1208 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1209 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1212 vtl->waypoint_color = data.c;
1213 if ( vtl->waypoint_gc )
1214 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1217 vtl->waypoint_text_color = data.c;
1218 if ( vtl->waypoint_text_gc )
1219 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1222 vtl->waypoint_bg_color = data.c;
1223 if ( vtl->waypoint_bg_gc )
1224 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1227 vtl->wpbgand = data.b;
1228 if ( vtl->waypoint_bg_gc )
1229 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1231 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1232 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1233 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1234 case PARAM_WPFONTSIZE:
1235 if ( data.u < FS_NUM_SIZES ) {
1236 vtl->wp_font_size = data.u;
1237 g_free ( vtl->wp_fsize_str );
1238 switch ( vtl->wp_font_size ) {
1239 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1240 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1241 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1242 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1243 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1244 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1245 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1249 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1251 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1252 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1253 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1254 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1260 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1262 VikLayerParamData rv;
1265 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1266 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1267 case PARAM_RV: rv.b = vtl->routes_visible; break;
1268 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1269 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1270 case PARAM_DM: rv.u = vtl->drawmode; break;
1271 case PARAM_TC: rv.c = vtl->track_color; break;
1272 case PARAM_DP: rv.b = vtl->drawpoints; break;
1273 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1274 case PARAM_DE: rv.b = vtl->drawelevation; break;
1275 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1276 case PARAM_DS: rv.b = vtl->drawstops; break;
1277 case PARAM_SL: rv.u = vtl->stop_length; break;
1278 case PARAM_DL: rv.b = vtl->drawlines; break;
1279 case PARAM_DD: rv.b = vtl->drawdirections; break;
1280 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1281 case PARAM_LT: rv.u = vtl->line_thickness; break;
1282 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1283 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1284 case PARAM_DI: rv.b = vtl->drawimages; break;
1285 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1286 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1287 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1288 case PARAM_IS: rv.u = vtl->image_size; break;
1289 case PARAM_IA: rv.u = vtl->image_alpha; break;
1290 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1291 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1292 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1293 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1294 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1295 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1296 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1297 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1298 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1299 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1301 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1302 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1303 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1304 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1310 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1312 // This '-3' is to account for the first few parameters not in the properties
1313 const gint OFFSET = -3;
1315 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1316 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1319 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1320 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1321 GtkWidget **ww2 = values[UI_CHG_LABELS];
1322 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1323 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1324 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1325 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1326 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1327 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1328 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1329 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1330 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1331 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1332 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1333 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1336 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1339 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1340 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1341 GtkWidget **ww2 = values[UI_CHG_LABELS];
1342 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1343 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1344 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1345 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1346 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1347 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1348 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1349 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1350 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1351 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1352 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1353 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1354 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1355 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1356 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1357 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1360 // Alter sensitivity of all track colours according to the draw track mode.
1363 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1364 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1365 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1366 GtkWidget **ww2 = values[UI_CHG_LABELS];
1367 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1368 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1369 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1370 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1373 case PARAM_MDTIME: {
1374 // Force metadata->timestamp to be always read-only for now.
1375 GtkWidget **ww = values[UI_CHG_WIDGETS];
1376 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1377 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1379 // NB Since other track settings have been split across tabs,
1380 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1385 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1392 // Use byte arrays to store sublayer data
1393 // much like done elsewhere e.g. vik_layer_marshall_params()
1394 GByteArray *ba = g_byte_array_new ( );
1399 guint object_length;
1402 // the length of the item
1403 // the sublayer type of item
1404 // the the actual item
1405 #define tlm_append(object_pointer, size, type) \
1407 object_length = (size); \
1408 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1409 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1410 g_byte_array_append ( ba, (object_pointer), object_length );
1412 // Layer parameters first
1413 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1414 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1415 g_byte_array_append ( ba, pd, pl );
1418 // Now sublayer data
1419 GHashTableIter iter;
1420 gpointer key, value;
1423 g_hash_table_iter_init ( &iter, vtl->waypoints );
1424 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1425 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1426 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1431 g_hash_table_iter_init ( &iter, vtl->tracks );
1432 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1433 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1434 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1439 g_hash_table_iter_init ( &iter, vtl->routes );
1440 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1441 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1442 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1452 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1454 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1456 gint consumed_length;
1458 // First the overall layer parameters
1459 memcpy(&pl, data, sizeof(pl));
1461 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1464 consumed_length = pl;
1465 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1467 #define tlm_size (*(gint *)data)
1468 // See marshalling above for order of how this is written
1470 data += sizeof_len_and_subtype + tlm_size;
1472 // Now the individual sublayers:
1474 while ( *data && consumed_length < len ) {
1475 // Normally four extra bytes at the end of the datastream
1476 // (since it's a GByteArray and that's where it's length is stored)
1477 // So only attempt read when there's an actual block of sublayer data
1478 if ( consumed_length + tlm_size < len ) {
1480 // Reuse pl to read the subtype from the data stream
1481 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1483 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1484 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1485 gchar *name = g_strdup ( trk->name );
1486 vik_trw_layer_add_track ( vtl, name, trk );
1489 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1490 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1491 gchar *name = g_strdup ( wp->name );
1492 vik_trw_layer_add_waypoint ( vtl, name, wp );
1495 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1496 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1497 gchar *name = g_strdup ( trk->name );
1498 vik_trw_layer_add_route ( vtl, name, trk );
1502 consumed_length += tlm_size + sizeof_len_and_subtype;
1505 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1507 // Not stored anywhere else so need to regenerate
1508 trw_layer_calculate_bounds_waypoints ( vtl );
1513 // Keep interesting hash function at least visible
1515 static guint strcase_hash(gconstpointer v)
1517 // 31 bit hash function
1520 gchar s[128]; // malloc is too slow for reading big files
1523 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1524 p[i] = toupper(t[i]);
1530 for (p += 1; *p != '\0'; p++)
1531 h = (h << 5) - h + *p;
1538 // Stick a 1 at the end of the function name to make it more unique
1539 // thus more easily searchable in a simple text editor
1540 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1542 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1543 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1545 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1546 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1548 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1549 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1550 // and with normal PC processing capabilities - it has negligibile performance impact
1551 // This also minimized the amount of rework - as the management of the hash tables already exists.
1553 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1554 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1555 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1557 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1558 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1559 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1560 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1561 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1562 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1564 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1566 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1568 // Param settings that are not available via the GUI
1569 // Force to on after processing params (which defaults them to off with a zero value)
1570 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1572 rv->metadata = vik_trw_metadata_new ();
1573 rv->draw_sync_done = TRUE;
1574 rv->draw_sync_do = TRUE;
1575 // Everything else is 0, FALSE or NULL
1581 static void trw_layer_free ( VikTrwLayer *trwlayer )
1583 g_hash_table_destroy(trwlayer->waypoints);
1584 g_hash_table_destroy(trwlayer->waypoints_iters);
1585 g_hash_table_destroy(trwlayer->tracks);
1586 g_hash_table_destroy(trwlayer->tracks_iters);
1587 g_hash_table_destroy(trwlayer->routes);
1588 g_hash_table_destroy(trwlayer->routes_iters);
1590 /* ODC: replace with GArray */
1591 trw_layer_free_track_gcs ( trwlayer );
1593 if ( trwlayer->wp_right_click_menu )
1594 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1596 if ( trwlayer->track_right_click_menu )
1597 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1599 if ( trwlayer->tracklabellayout != NULL)
1600 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1602 if ( trwlayer->wplabellayout != NULL)
1603 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1605 if ( trwlayer->waypoint_gc != NULL )
1606 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1608 if ( trwlayer->waypoint_text_gc != NULL )
1609 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1611 if ( trwlayer->waypoint_bg_gc != NULL )
1612 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1614 g_free ( trwlayer->wp_fsize_str );
1615 g_free ( trwlayer->track_fsize_str );
1617 if ( trwlayer->tpwin != NULL )
1618 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1620 if ( trwlayer->tracks_analysis_dialog != NULL )
1621 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1623 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1624 g_queue_free ( trwlayer->image_cache );
1627 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1631 dp->highlight = highlight;
1632 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1633 dp->xmpp = vik_viewport_get_xmpp ( vp );
1634 dp->ympp = vik_viewport_get_ympp ( vp );
1635 dp->width = vik_viewport_get_width ( vp );
1636 dp->height = vik_viewport_get_height ( vp );
1637 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1638 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1640 dp->center = vik_viewport_get_center ( vp );
1641 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1642 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1647 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1648 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1649 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1651 dp->ce1 = dp->center->east_west-w2;
1652 dp->ce2 = dp->center->east_west+w2;
1653 dp->cn1 = dp->center->north_south-h2;
1654 dp->cn2 = dp->center->north_south+h2;
1655 } else if ( dp->lat_lon ) {
1656 VikCoord upperleft, bottomright;
1657 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1658 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1659 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1660 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1661 dp->ce1 = upperleft.east_west;
1662 dp->ce2 = bottomright.east_west;
1663 dp->cn1 = bottomright.north_south;
1664 dp->cn2 = upperleft.north_south;
1667 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1671 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1672 * Here a simple traffic like light colour system is used:
1673 * . slow points are red
1674 * . average is yellow
1675 * . fast points are green
1677 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1680 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1681 if ( average_speed > 0 ) {
1682 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1683 if ( rv < low_speed )
1684 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1685 else if ( rv > high_speed )
1686 return VIK_TRW_LAYER_TRACK_GC_FAST;
1688 return VIK_TRW_LAYER_TRACK_GC_AVER;
1691 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1694 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1696 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1697 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1698 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1699 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1703 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1705 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1707 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1708 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1710 // Fallback if parse failure
1711 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1713 g_free ( label_markup );
1715 gint label_x, label_y;
1717 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1719 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1720 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1724 * distance_in_preferred_units:
1725 * @dist: The source distance in standard SI Units (i.e. metres)
1727 * TODO: This is a generic function that could be moved into globals.c or utils.c
1729 * Probably best used if you have a only few conversions to perform.
1730 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1731 * since it will be doing the preference check on each call
1733 * Returns: The distance in the units as specified by the preferences
1735 static gdouble distance_in_preferred_units ( gdouble dist )
1738 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1739 switch (dist_units) {
1740 case VIK_UNITS_DISTANCE_MILES:
1741 mydist = VIK_METERS_TO_MILES(dist);
1743 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1744 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1746 // VIK_UNITS_DISTANCE_KILOMETRES:
1748 mydist = dist/1000.0;
1755 * trw_layer_draw_dist_labels:
1757 * Draw a few labels along a track at nicely seperated distances
1758 * This might slow things down if there's many tracks being displayed with this on.
1760 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1762 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1763 25.0, 40.0, 50.0, 75.0, 100.0,
1764 150.0, 200.0, 250.0, 500.0, 1000.0};
1766 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1768 // Convert to specified unit to find the friendly breakdown value
1769 dist = distance_in_preferred_units ( dist );
1773 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1774 if ( chunksd[i] > dist ) {
1776 dist = chunksd[index];
1781 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1783 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1784 gdouble dist_i = dist * i;
1786 // Convert distance back into metres for use in finding a trackpoint
1787 switch (dist_units) {
1788 case VIK_UNITS_DISTANCE_MILES:
1789 dist_i = VIK_MILES_TO_METERS(dist_i);
1791 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1792 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1794 // VIK_UNITS_DISTANCE_KILOMETRES:
1796 dist_i = dist_i*1000.0;
1800 gdouble dist_current = 0.0;
1801 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1802 gdouble dist_next = 0.0;
1803 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1805 gdouble dist_between_tps = fabs (dist_next - dist_current);
1806 gdouble ratio = 0.0;
1807 // Prevent division by 0 errors
1808 if ( dist_between_tps > 0.0 )
1809 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1811 if ( tp_current && tp_next ) {
1812 // Construct the name based on the distance value
1815 switch (dist_units) {
1816 case VIK_UNITS_DISTANCE_MILES:
1817 units = g_strdup ( _("miles") );
1819 // VIK_UNITS_DISTANCE_KILOMETRES:
1821 units = g_strdup ( _("km") );
1825 // Convert for display
1826 dist_i = distance_in_preferred_units ( dist_i );
1828 // Make the precision of the output related to the unit size.
1830 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1831 else if ( index == 1 )
1832 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1834 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1837 struct LatLon ll_current, ll_next;
1838 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1839 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1841 // positional interpolation
1842 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1843 // but should be good enough over the small scale that I anticipate usage on
1844 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1845 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1847 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1850 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1851 fgcolour = gdk_color_to_string ( &(trk->color) );
1853 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1855 // if highlight mode on, then colour the background in the highlight colour
1857 if ( drawing_highlight )
1858 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1860 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1862 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1864 g_free ( fgcolour );
1865 g_free ( bgcolour );
1872 * trw_layer_draw_track_name_labels:
1874 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1876 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1879 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1880 fgcolour = gdk_color_to_string ( &(trk->color) );
1882 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1884 // if highlight mode on, then colour the background in the highlight colour
1886 if ( drawing_highlight )
1887 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1889 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1891 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1893 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1894 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1895 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1896 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1897 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1898 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1900 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1902 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1905 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1906 // No other labels to draw
1909 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1912 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1915 VikCoord begin_coord = tp_begin->coord;
1916 VikCoord end_coord = tp_end->coord;
1918 gboolean done_start_end = FALSE;
1920 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1921 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1923 // This number can be configured via the settings if you really want to change it
1924 gdouble distance_diff;
1925 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1926 distance_diff = 100.0; // Metres
1928 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1929 // Start and end 'close' together so only draw one label at an average location
1930 gint x1, x2, y1, y2;
1931 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1932 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1934 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1936 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1937 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1940 done_start_end = TRUE;
1944 if ( ! done_start_end ) {
1945 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1946 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1947 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1948 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1949 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1950 g_free ( name_start );
1952 // Don't draw end label if this is the one being created
1953 if ( trk != dp->vtl->current_track ) {
1954 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1955 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1956 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1957 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1958 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1959 g_free ( name_end );
1964 g_free ( fgcolour );
1965 g_free ( bgcolour );
1969 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1971 if ( ! track->visible )
1974 /* TODO: this function is a mess, get rid of any redundancy */
1975 GList *list = track->trackpoints;
1977 gboolean useoldvals = TRUE;
1979 gboolean drawpoints;
1981 gboolean drawelevation;
1982 gdouble min_alt, max_alt, alt_diff = 0;
1984 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1985 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1988 if ( dp->vtl->drawelevation )
1990 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1991 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1992 alt_diff = max_alt - min_alt;
1995 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1996 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1997 trw_layer_draw_track ( id, track, dp, TRUE );
1999 if ( draw_track_outline )
2000 drawpoints = drawstops = FALSE;
2002 drawpoints = dp->vtl->drawpoints;
2003 drawstops = dp->vtl->drawstops;
2006 gboolean drawing_highlight = FALSE;
2007 /* Current track - used for creation */
2008 if ( track == dp->vtl->current_track )
2009 main_gc = dp->vtl->current_track_gc;
2011 if ( dp->highlight ) {
2012 /* Draw all tracks of the layer in special colour
2013 NB this supercedes the drawmode */
2014 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2015 drawing_highlight = TRUE;
2017 if ( !drawing_highlight ) {
2018 // Still need to figure out the gc according to the drawing mode:
2019 switch ( dp->vtl->drawmode ) {
2020 case DRAWMODE_BY_TRACK:
2021 if ( dp->vtl->track_1color_gc )
2022 g_object_unref ( dp->vtl->track_1color_gc );
2023 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2024 main_gc = dp->vtl->track_1color_gc;
2027 // Mostly for DRAWMODE_ALL_SAME_COLOR
2028 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2029 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2036 int x, y, oldx, oldy;
2037 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2039 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2041 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2043 // Draw the first point as something a bit different from the normal points
2044 // ATM it's slightly bigger and a triangle
2046 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2047 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2053 gdouble average_speed = 0.0;
2054 gdouble low_speed = 0.0;
2055 gdouble high_speed = 0.0;
2056 // If necessary calculate these values - which is done only once per track redraw
2057 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2058 // the percentage factor away from the average speed determines transistions between the levels
2059 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2060 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2061 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2064 while ((list = g_list_next(list)))
2066 tp = VIK_TRACKPOINT(list->data);
2067 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2069 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2070 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2071 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2072 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2073 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2075 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2078 * If points are the same in display coordinates, don't draw.
2080 if ( useoldvals && x == oldx && y == oldy )
2082 // Still need to process points to ensure 'stops' are drawn if required
2083 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2084 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2085 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2090 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2091 if ( drawpoints || dp->vtl->drawlines ) {
2092 // setup main_gc for both point and line drawing
2093 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2094 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
2098 if ( drawpoints && ! draw_track_outline )
2103 * The concept of drawing stops is that a trackpoint
2104 * that is if the next trackpoint has a timestamp far into
2105 * the future, we draw a circle of 6x trackpoint size,
2106 * instead of a rectangle of 2x trackpoint size.
2107 * This is drawn first so the trackpoint will be drawn on top
2110 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2111 /* Stop point. Draw 6x circle. Always in redish colour */
2112 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2114 /* Regular point - draw 2x square. */
2115 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2118 /* Final point - draw 4x circle. */
2119 vik_viewport_draw_arc ( dp->vp, main_gc, TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 );
2122 if ((!tp->newsegment) && (dp->vtl->drawlines))
2125 /* UTM only: zone check */
2126 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2127 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2130 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2132 if ( draw_track_outline ) {
2133 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2137 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2139 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2141 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2146 tmp[1].y = oldy-FIXALTITUDE(list->data);
2148 tmp[2].y = y-FIXALTITUDE(list->next->data);
2153 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2154 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2156 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2157 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2159 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2164 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2165 // Draw an arrow at the mid point to show the direction of the track
2166 // Code is a rework from vikwindow::draw_ruler()
2167 gint midx = (oldx + x) / 2;
2168 gint midy = (oldy + y) / 2;
2170 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2171 // Avoid divide by zero and ensure at least 1 pixel big
2173 gdouble dx = (oldx - midx) / len;
2174 gdouble dy = (oldy - midy) / len;
2175 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2176 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2186 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2188 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2189 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2191 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2193 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2194 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
2198 * If points are the same in display coordinates, don't draw.
2200 if ( x != oldx || y != oldy )
2202 if ( draw_track_outline )
2203 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2205 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2211 * If points are the same in display coordinates, don't draw.
2213 if ( x != oldx && y != oldy )
2215 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2216 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2224 // Labels drawn after the trackpoints, so the labels are on top
2225 if ( dp->vtl->track_draw_labels ) {
2226 if ( track->max_number_dist_labels > 0 ) {
2227 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2230 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2231 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2237 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2239 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2240 trw_layer_draw_track ( id, track, dp, FALSE );
2244 static void cached_pixbuf_free ( CachedPixbuf *cp )
2246 g_object_unref ( G_OBJECT(cp->pixbuf) );
2247 g_free ( cp->image );
2250 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2252 return strcmp ( cp->image, name );
2255 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2258 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2259 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2260 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2263 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2265 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2267 if ( wp->image && dp->vtl->drawimages )
2269 GdkPixbuf *pixbuf = NULL;
2272 if ( dp->vtl->image_alpha == 0)
2275 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2277 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2280 gchar *image = wp->image;
2281 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2282 if ( ! regularthumb )
2284 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2285 image = "\x12\x00"; /* this shouldn't occur naturally. */
2289 CachedPixbuf *cp = NULL;
2290 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2291 if ( dp->vtl->image_size == 128 )
2292 cp->pixbuf = regularthumb;
2295 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2296 g_assert ( cp->pixbuf );
2297 g_object_unref ( G_OBJECT(regularthumb) );
2299 cp->image = g_strdup ( image );
2301 /* needed so 'click picture' tool knows how big the pic is; we don't
2302 * store it in cp because they may have been freed already. */
2303 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2304 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2306 g_queue_push_head ( dp->vtl->image_cache, cp );
2307 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2308 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2310 pixbuf = cp->pixbuf;
2314 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2320 w = gdk_pixbuf_get_width ( pixbuf );
2321 h = gdk_pixbuf_get_height ( pixbuf );
2323 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2325 if ( dp->highlight ) {
2326 // Highlighted - so draw a little border around the chosen one
2327 // single line seems a little weak so draw 2 of them
2328 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2329 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2330 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2331 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2334 if ( dp->vtl->image_alpha == 255 )
2335 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2337 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2339 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2343 // Draw appropriate symbol - either symbol image or simple types
2344 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2345 vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
2347 else if ( wp == dp->vtl->current_wp ) {
2348 switch ( dp->vtl->wp_symbol ) {
2349 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2350 case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2351 case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
2352 case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 );
2353 vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 );
2358 switch ( dp->vtl->wp_symbol ) {
2359 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
2360 case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
2361 case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x-dp->vtl->wp_size/2, y-dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
2362 case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y-dp->vtl->wp_size, x+dp->vtl->wp_size, y+dp->vtl->wp_size );
2363 vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y+dp->vtl->wp_size, x+dp->vtl->wp_size, y-dp->vtl->wp_size ); break;
2368 if ( dp->vtl->drawlabels )
2370 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2371 gint label_x, label_y;
2373 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2375 // Could this stored in the waypoint rather than recreating each pass?
2376 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2378 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2379 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2381 // Fallback if parse failure
2382 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2384 g_free ( wp_label_markup );
2386 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2387 label_x = x - width/2;
2388 if ( wp->symbol_pixbuf )
2389 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2391 label_y = y - dp->vtl->wp_size - height - 2;
2393 /* if highlight mode on, then draw background text in highlight colour */
2394 if ( dp->highlight )
2395 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2397 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2398 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2403 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2405 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2406 trw_layer_draw_waypoint ( id, wp, dp );
2410 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2412 static struct DrawingParams dp;
2413 g_assert ( l != NULL );
2415 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2417 if ( l->tracks_visible )
2418 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2420 if ( l->routes_visible )
2421 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2423 if (l->waypoints_visible)
2424 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2427 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2429 // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2430 // This may seem slightly inefficient to test each time for every layer
2431 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2432 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2433 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2435 trw_layer_draw_with_highlight ( l, data, FALSE );
2438 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2440 // Check the layer for visibility (including all the parents visibilities)
2441 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2443 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2447 * vik_trw_layer_draw_highlight_item:
2449 * Only handles a single track or waypoint ATM
2450 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2452 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2454 // Check the layer for visibility (including all the parents visibilities)
2455 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2458 static struct DrawingParams dp;
2459 init_drawing_params ( &dp, vtl, vvp, TRUE );
2462 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2464 trw_layer_draw_track_cb ( NULL, trk, &dp );
2466 if ( vtl->waypoints_visible && wpt ) {
2467 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2472 * vik_trw_layer_draw_highlight_item:
2474 * Generally for drawing all tracks or routes or waypoints
2475 * trks may be actually routes
2476 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2478 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2480 // Check the layer for visibility (including all the parents visibilities)
2481 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2484 static struct DrawingParams dp;
2485 init_drawing_params ( &dp, vtl, vvp, TRUE );
2488 gboolean is_routes = (trks == vtl->routes);
2489 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2491 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2494 if ( vtl->waypoints_visible && wpts )
2495 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2498 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2501 if ( vtl->track_bg_gc )
2503 g_object_unref ( vtl->track_bg_gc );
2504 vtl->track_bg_gc = NULL;
2506 if ( vtl->track_1color_gc )
2508 g_object_unref ( vtl->track_1color_gc );
2509 vtl->track_1color_gc = NULL;
2511 if ( vtl->current_track_gc )
2513 g_object_unref ( vtl->current_track_gc );
2514 vtl->current_track_gc = NULL;
2516 if ( vtl->current_track_newpoint_gc )
2518 g_object_unref ( vtl->current_track_newpoint_gc );
2519 vtl->current_track_newpoint_gc = NULL;
2522 if ( ! vtl->track_gc )
2524 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2525 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2526 g_array_free ( vtl->track_gc, TRUE );
2527 vtl->track_gc = NULL;
2530 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2532 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2533 gint width = vtl->line_thickness;
2535 if ( vtl->track_gc )
2536 trw_layer_free_track_gcs ( vtl );
2538 if ( vtl->track_bg_gc )
2539 g_object_unref ( vtl->track_bg_gc );
2540 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2542 // Ensure new track drawing heeds line thickness setting
2543 // however always have a minium of 2, as 1 pixel is really narrow
2544 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2546 if ( vtl->current_track_gc )
2547 g_object_unref ( vtl->current_track_gc );
2548 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2549 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2551 // 'newpoint' gc is exactly the same as the current track gc
2552 if ( vtl->current_track_newpoint_gc )
2553 g_object_unref ( vtl->current_track_newpoint_gc );
2554 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2555 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2557 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2559 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2560 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2562 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2563 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2564 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2566 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2568 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2571 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2573 VikTrwLayer *rv = trw_layer_new1 ( vp );
2574 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2576 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2577 /* early exit, as the rest is GUI related */
2581 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2582 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2584 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2585 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2587 trw_layer_new_track_gcs ( rv, vp );
2589 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2590 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2591 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2592 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2594 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2596 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2601 #define SMALL_ICON_SIZE 18
2603 * Can accept a null symbol, and may return null value
2605 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2607 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2608 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2609 // So needing a small icon for the treeview may need some resizing:
2610 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2611 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2615 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2617 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2619 GdkPixbuf *pixbuf = NULL;
2621 if ( track->has_color ) {
2622 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2623 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2624 // Here is some magic found to do the conversion
2625 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2626 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2627 ((track->color.green & 0xff00) << 8) |
2628 (track->color.blue & 0xff00);
2630 gdk_pixbuf_fill ( pixbuf, pixel );
2633 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
2636 g_object_unref (pixbuf);
2638 *new_iter = *((GtkTreeIter *) pass_along[1]);
2639 if ( track->is_route )
2640 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2642 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2644 if ( ! track->visible )
2645 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2648 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2650 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2652 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
2654 *new_iter = *((GtkTreeIter *) pass_along[1]);
2655 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2657 if ( ! wp->visible )
2658 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2661 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2663 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2666 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2668 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2671 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2673 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2676 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2679 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2681 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2682 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2684 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2686 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2689 if ( g_hash_table_size (vtl->routes) > 0 ) {
2690 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2692 pass_along[0] = &(vtl->routes_iter);
2693 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2695 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2697 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2700 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2701 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2703 pass_along[0] = &(vtl->waypoints_iter);
2704 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2706 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2708 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2713 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2717 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2718 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2719 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2720 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2722 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2724 return (t->visible ^= 1);
2728 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2730 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2732 return (t->visible ^= 1);
2736 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2738 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2740 return (t->visible ^= 1);
2750 * Return a property about tracks for this layer
2752 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2754 return vtl->line_thickness;
2757 // Structure to hold multiple track information for a layer
2766 * Build up layer multiple track information via updating the tooltip_tracks structure
2768 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2770 tt->length = tt->length + vik_track_get_length (tr);
2772 // Ensure times are available
2773 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2774 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2775 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2776 if ( trkpt_last->has_timestamp ) {
2778 t1 = vik_track_get_tp_first(tr)->timestamp;
2779 t2 = trkpt_last->timestamp;
2781 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2782 // Hence initialize to the first 'proper' value
2783 if ( tt->start_time == 0 )
2784 tt->start_time = t1;
2785 if ( tt->end_time == 0 )
2788 // Update find the earliest / last times
2789 if ( t1 < tt->start_time )
2790 tt->start_time = t1;
2791 if ( t2 > tt->end_time )
2794 // Keep track of total time
2795 // there maybe gaps within a track (eg segments)
2796 // but this should be generally good enough for a simple indicator
2797 tt->duration = tt->duration + (int)(t2-t1);
2803 * Generate tooltip text for the layer.
2804 * This is relatively complicated as it considers information for
2805 * no tracks, a single track or multiple tracks
2806 * (which may or may not have timing information)
2808 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2819 static gchar tmp_buf[128];
2822 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2824 // Safety check - I think these should always be valid
2825 if ( vtl->tracks && vtl->waypoints ) {
2826 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2827 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2829 GDate* gdate_start = g_date_new ();
2830 g_date_set_time_t (gdate_start, tt.start_time);
2832 GDate* gdate_end = g_date_new ();
2833 g_date_set_time_t (gdate_end, tt.end_time);
2835 if ( g_date_compare (gdate_start, gdate_end) ) {
2836 // Dates differ so print range on separate line
2837 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2838 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2839 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2842 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2843 if ( tt.start_time != 0 )
2844 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2848 if ( tt.length > 0.0 ) {
2849 gdouble len_in_units;
2851 // Setup info dependent on distance units
2852 switch ( a_vik_get_units_distance() ) {
2853 case VIK_UNITS_DISTANCE_MILES:
2854 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2855 len_in_units = VIK_METERS_TO_MILES(tt.length);
2857 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2858 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2859 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2862 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2863 len_in_units = tt.length/1000.0;
2867 // Timing information if available
2869 if ( tt.duration > 0 ) {
2870 g_snprintf (tbuf1, sizeof(tbuf1),
2871 _(" in %d:%02d hrs:mins"),
2872 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2874 g_snprintf (tbuf2, sizeof(tbuf2),
2875 _("\n%sTotal Length %.1f %s%s"),
2876 tbuf3, len_in_units, tbuf4, tbuf1);
2879 // Put together all the elements to form compact tooltip text
2880 g_snprintf (tmp_buf, sizeof(tmp_buf),
2881 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2882 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2884 g_date_free (gdate_start);
2885 g_date_free (gdate_end);
2892 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2896 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2898 // Very simple tooltip - may expand detail in the future...
2899 static gchar tmp_buf[32];
2900 g_snprintf (tmp_buf, sizeof(tmp_buf),
2902 g_hash_table_size (l->tracks));
2906 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2908 // Very simple tooltip - may expand detail in the future...
2909 static gchar tmp_buf[32];
2910 g_snprintf (tmp_buf, sizeof(tmp_buf),
2912 g_hash_table_size (l->routes));
2917 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2918 // Same tooltip for a route
2919 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2923 tr = g_hash_table_lookup ( l->tracks, sublayer );
2925 tr = g_hash_table_lookup ( l->routes, sublayer );
2928 // Could be a better way of handling strings - but this works...
2929 gchar time_buf1[20];
2930 gchar time_buf2[20];
2931 time_buf1[0] = '\0';
2932 time_buf2[0] = '\0';
2933 static gchar tmp_buf[100];
2934 // Compact info: Short date eg (11/20/99), duration and length
2935 // Hopefully these are the things that are most useful and so promoted into the tooltip
2936 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2937 // %x The preferred date representation for the current locale without the time.
2938 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2939 time_t dur = vik_track_get_duration ( tr );
2941 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2943 // Get length and consider the appropriate distance units
2944 gdouble tr_len = vik_track_get_length(tr);
2945 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2946 switch (dist_units) {
2947 case VIK_UNITS_DISTANCE_KILOMETRES:
2948 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2950 case VIK_UNITS_DISTANCE_MILES:
2951 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2953 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2954 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
2963 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2965 // Very simple tooltip - may expand detail in the future...
2966 static gchar tmp_buf[32];
2967 g_snprintf (tmp_buf, sizeof(tmp_buf),
2969 g_hash_table_size (l->waypoints));
2973 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2975 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2976 // NB It's OK to return NULL
2981 return w->description;
2990 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2993 * set_statusbar_msg_info_trkpt:
2995 * Function to show track point information on the statusbar
2996 * Items displayed is controlled by the settings format code
2998 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
3000 gchar *statusbar_format_code = NULL;
3001 gboolean need2free = FALSE;
3002 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3003 // Otherwise use default
3004 statusbar_format_code = g_strdup ( "KEATDN" );
3008 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
3009 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3013 g_free ( statusbar_format_code );
3017 * Function to show basic waypoint information on the statusbar
3019 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3022 switch (a_vik_get_units_height ()) {
3023 case VIK_UNITS_HEIGHT_FEET:
3024 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3027 //VIK_UNITS_HEIGHT_METRES:
3028 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3032 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3033 // one can easily use the current pointer position to see this if needed
3034 gchar *lat = NULL, *lon = NULL;
3035 static struct LatLon ll;
3036 vik_coord_to_latlon (&(wpt->coord), &ll);
3037 a_coords_latlon_to_string ( &ll, &lat, &lon );
3039 // Combine parts to make overall message
3042 // Add comment if available
3043 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3045 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3046 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3053 * General layer selection function, find out which bit is selected and take appropriate action
3055 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3058 l->current_wp = NULL;
3059 l->current_wp_id = NULL;
3060 trw_layer_cancel_current_tp ( l, FALSE );
3063 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3067 case VIK_TREEVIEW_TYPE_LAYER:
3069 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3070 /* Mark for redraw */
3075 case VIK_TREEVIEW_TYPE_SUBLAYER:
3079 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3081 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3082 /* Mark for redraw */
3086 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3088 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3089 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3090 /* Mark for redraw */
3094 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3096 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3097 /* Mark for redraw */
3101 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3103 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3104 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3105 /* Mark for redraw */
3109 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3111 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3112 /* Mark for redraw */
3116 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3118 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3120 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3121 // Show some waypoint info
3122 set_statusbar_msg_info_wpt ( l, wpt );
3123 /* Mark for redraw */
3130 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3139 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3144 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3149 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3154 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3156 return l->waypoints;
3159 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3161 return vtl->tracks_iters;
3164 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3166 return vtl->routes_iters;
3169 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3171 return vtl->waypoints;
3174 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3176 return ! ( g_hash_table_size ( vtl->tracks ) ||
3177 g_hash_table_size ( vtl->routes ) ||
3178 g_hash_table_size ( vtl->waypoints ) );
3181 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3183 return vtl->tracks_visible;
3186 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3188 return vtl->routes_visible;
3191 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3193 return vtl->waypoints_visible;
3197 * ATM use a case sensitive find
3198 * Finds the first one
3200 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3202 if ( wp && wp->name )
3203 if ( ! strcmp ( wp->name, name ) )
3209 * Get waypoint by name - not guaranteed to be unique
3210 * Finds the first one
3212 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3214 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3218 * ATM use a case sensitive find
3219 * Finds the first one
3221 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3223 if ( trk && trk->name )
3224 if ( ! strcmp ( trk->name, name ) )
3230 * Get track by name - not guaranteed to be unique
3231 * Finds the first one
3233 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3235 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3239 * Get route by name - not guaranteed to be unique
3240 * Finds the first one
3242 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3244 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3247 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3249 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3250 maxmin[0].lat = trk->bbox.north;
3251 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3252 maxmin[1].lat = trk->bbox.south;
3253 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3254 maxmin[0].lon = trk->bbox.east;
3255 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3256 maxmin[1].lon = trk->bbox.west;
3259 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3261 // Continually reuse maxmin to find the latest maximum and minimum values
3262 // First set to waypoints bounds
3263 maxmin[0].lat = vtl->waypoints_bbox.north;
3264 maxmin[1].lat = vtl->waypoints_bbox.south;
3265 maxmin[0].lon = vtl->waypoints_bbox.east;
3266 maxmin[1].lon = vtl->waypoints_bbox.west;
3267 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3268 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3271 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3273 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. like I don't have more important things to worry about... */
3274 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3275 trw_layer_find_maxmin (vtl, maxmin);
3276 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3280 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3281 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3286 static void trw_layer_centerize ( menu_array_layer values )
3288 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3290 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3291 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3293 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3296 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3298 /* First set the center [in case previously viewing from elsewhere] */
3299 /* Then loop through zoom levels until provided positions are in view */
3300 /* This method is not particularly fast - but should work well enough */
3301 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3303 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3304 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3306 /* Convert into definite 'smallest' and 'largest' positions */
3307 struct LatLon minmin;
3308 if ( maxmin[0].lat < maxmin[1].lat )
3309 minmin.lat = maxmin[0].lat;
3311 minmin.lat = maxmin[1].lat;
3313 struct LatLon maxmax;
3314 if ( maxmin[0].lon > maxmin[1].lon )
3315 maxmax.lon = maxmin[0].lon;
3317 maxmax.lon = maxmin[1].lon;
3319 /* Never zoom in too far - generally not that useful, as too close ! */
3320 /* Always recalculate the 'best' zoom level */
3322 vik_viewport_set_zoom ( vvp, zoom );
3324 gdouble min_lat, max_lat, min_lon, max_lon;
3325 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3326 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3327 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3328 /* NB I think the logic used in this test to determine if the bounds is within view
3329 fails if track goes across 180 degrees longitude.
3330 Hopefully that situation is not too common...
3331 Mind you viking doesn't really do edge locations to well anyway */
3332 if ( min_lat < minmin.lat &&
3333 max_lat > minmin.lat &&
3334 min_lon < maxmax.lon &&
3335 max_lon > maxmax.lon )
3336 /* Found within zoom level */
3341 vik_viewport_set_zoom ( vvp, zoom );
3345 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3347 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3348 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3349 trw_layer_find_maxmin (vtl, maxmin);
3350 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3353 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3358 static void trw_layer_auto_view ( menu_array_layer values )
3360 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3361 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3362 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3363 vik_layers_panel_emit_update ( vlp );
3366 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3369 static void trw_layer_export_gpspoint ( menu_array_layer values )
3371 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3373 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3375 g_free ( auto_save_name );
3378 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3380 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3382 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3384 g_free ( auto_save_name );
3387 static void trw_layer_export_gpx ( menu_array_layer values )
3389 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3391 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3393 g_free ( auto_save_name );
3396 static void trw_layer_export_kml ( menu_array_layer values )
3398 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3400 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3402 g_free ( auto_save_name );
3405 static void trw_layer_export_geojson ( menu_array_layer values )
3407 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3409 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3411 g_free ( auto_save_name );
3414 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3416 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3417 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3420 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3422 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3425 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3427 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3430 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3432 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3434 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3435 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3437 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3439 if ( !trk || !trk->name )
3442 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3444 gchar *label = NULL;
3445 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3446 label = _("Export Route as GPX");
3448 label = _("Export Track as GPX");
3449 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3451 g_free ( auto_save_name );
3454 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3456 wpu_udata *user_data = udata;
3457 if ( wp == user_data->wp ) {
3458 user_data->uuid = id;
3464 static void trw_layer_goto_wp ( menu_array_layer values )
3466 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3467 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3468 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3469 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3470 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3472 GTK_RESPONSE_REJECT,
3474 GTK_RESPONSE_ACCEPT,
3477 GtkWidget *label, *entry;
3478 label = gtk_label_new(_("Waypoint Name:"));
3479 entry = gtk_entry_new();
3481 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3482 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3483 gtk_widget_show_all ( label );
3484 gtk_widget_show_all ( entry );
3486 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3488 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3490 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3491 // Find *first* wp with the given name
3492 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3495 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3498 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3499 vik_layers_panel_emit_update ( vlp );
3501 // Find and select on the side panel
3506 // Hmmm, want key of it
3507 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3509 if ( wpf && udata.uuid ) {
3510 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3511 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3520 gtk_widget_destroy ( dia );
3523 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3525 gchar *default_name = highest_wp_number_get(vtl);
3526 VikWaypoint *wp = vik_waypoint_new();
3527 gchar *returned_name;
3529 wp->coord = *def_coord;
3531 // Attempt to auto set height if DEM data is available
3532 vik_waypoint_apply_dem_data ( wp, TRUE );
3534 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3536 if ( returned_name )
3539 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3540 g_free (default_name);
3541 g_free (returned_name);
3544 g_free (default_name);
3545 vik_waypoint_free(wp);
3549 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3551 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3552 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3553 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3554 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3555 VikViewport *vvp = vik_window_viewport(vw);
3557 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3558 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3559 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3560 trw_layer_calculate_bounds_waypoints ( vtl );
3561 vik_layers_panel_emit_update ( vlp );
3564 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3567 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3568 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3570 trw_layer_find_maxmin (vtl, maxmin);
3571 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3572 trw_layer_calculate_bounds_waypoints ( vtl );
3573 vik_layers_panel_emit_update ( vlp );
3576 #ifdef VIK_CONFIG_GEOTAG
3577 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3579 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3581 // Update directly - not changing the mtime
3582 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3585 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3587 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3590 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3594 * Use code in separate file for this feature as reasonably complex
3596 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3598 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3599 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3600 // Unset so can be reverified later if necessary
3601 vtl->has_verified_thumbnails = FALSE;
3603 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3609 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3611 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3612 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3614 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3620 static void trw_layer_geotagging ( menu_array_layer values )
3622 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3623 // Unset so can be reverified later if necessary
3624 vtl->has_verified_thumbnails = FALSE;
3626 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3633 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3635 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3637 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3638 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3639 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3640 VikViewport *vvp = vik_window_viewport(vw);
3642 vik_datasource_mode_t mode = datasource->mode;
3643 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3644 mode = VIK_DATASOURCE_ADDTOLAYER;
3645 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3649 * Acquire into this TRW Layer straight from GPS Device
3651 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3653 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3657 * Acquire into this TRW Layer from Directions
3659 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3661 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3665 * Acquire into this TRW Layer from an entered URL
3667 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3669 trw_layer_acquire ( values, &vik_datasource_url_interface );
3672 #ifdef VIK_CONFIG_OPENSTREETMAP
3674 * Acquire into this TRW Layer from OSM
3676 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3678 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3682 * Acquire into this TRW Layer from OSM for 'My' Traces
3684 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3686 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3690 #ifdef VIK_CONFIG_GEOCACHES
3692 * Acquire into this TRW Layer from Geocaching.com
3694 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3696 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3700 #ifdef VIK_CONFIG_GEOTAG
3702 * Acquire into this TRW Layer from images
3704 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3706 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3708 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3710 // Reverify thumbnails as they may have changed
3711 vtl->has_verified_thumbnails = FALSE;
3712 trw_layer_verify_thumbnails ( vtl, NULL );
3717 * Acquire into this TRW Layer from any GPS Babel supported file
3719 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3721 trw_layer_acquire ( values, &vik_datasource_file_interface );
3724 static void trw_layer_gps_upload ( menu_array_layer values )
3726 menu_array_sublayer data;
3728 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3730 data[MA_VTL] = values[MA_VTL];
3731 data[MA_VLP] = values[MA_VLP];
3733 trw_layer_gps_upload_any ( data );
3737 * If pass_along[3] is defined that this will upload just that track
3739 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3741 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3742 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3744 // May not actually get a track here as values[2&3] can be null
3745 VikTrack *track = NULL;
3746 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3747 gboolean xfer_all = FALSE;
3749 if ( values[MA_SUBTYPE] ) {
3751 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3752 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3755 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3756 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3759 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3762 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3766 else if ( !values[MA_CONFIRM] )
3767 xfer_all = TRUE; // i.e. whole layer
3769 if (track && !track->visible) {
3770 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3774 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3775 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3776 GTK_DIALOG_DESTROY_WITH_PARENT,
3778 GTK_RESPONSE_ACCEPT,
3780 GTK_RESPONSE_REJECT,
3783 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3784 GtkWidget *response_w = NULL;
3785 #if GTK_CHECK_VERSION (2, 20, 0)
3786 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3790 gtk_widget_grab_focus ( response_w );
3792 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3794 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3795 datasource_gps_clean_up ( dgs );
3796 gtk_widget_destroy ( dialog );
3800 // Get info from reused datasource dialog widgets
3801 gchar* protocol = datasource_gps_get_protocol ( dgs );
3802 gchar* port = datasource_gps_get_descriptor ( dgs );
3803 // NB don't free the above strings as they're references to values held elsewhere
3804 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3805 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3806 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3807 gboolean turn_off = datasource_gps_get_off ( dgs );
3809 gtk_widget_destroy ( dialog );
3811 // When called from the viewport - work the corresponding layerspanel:
3813 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3816 // Apply settings to transfer to the GPS device
3823 vik_layers_panel_get_viewport (vlp),
3831 static void trw_layer_new_wp ( menu_array_layer values )
3833 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3834 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3835 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3836 instead return true if you want to update. */
3837 if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) && VIK_LAYER(vtl)->visible ) {
3838 trw_layer_calculate_bounds_waypoints ( vtl );
3839 vik_layers_panel_emit_update ( vlp );
3843 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3845 vtl->current_track = vik_track_new();
3846 vik_track_set_defaults ( vtl->current_track );
3847 vtl->current_track->visible = TRUE;
3848 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3849 // Create track with the preferred colour from the layer properties
3850 vtl->current_track->color = vtl->track_color;
3852 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3853 vtl->current_track->has_color = TRUE;
3854 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3857 static void trw_layer_new_track ( menu_array_layer values )
3859 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3861 if ( ! vtl->current_track ) {
3862 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3863 new_track_create_common ( vtl, name );
3866 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3870 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3872 vtl->current_track = vik_track_new();
3873 vik_track_set_defaults ( vtl->current_track );
3874 vtl->current_track->visible = TRUE;
3875 vtl->current_track->is_route = TRUE;
3876 // By default make all routes red
3877 vtl->current_track->has_color = TRUE;
3878 gdk_color_parse ( "red", &vtl->current_track->color );
3879 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3882 static void trw_layer_new_route ( menu_array_layer values )
3884 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3886 if ( ! vtl->current_track ) {
3887 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3888 new_route_create_common ( vtl, name );
3890 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3894 static void trw_layer_auto_routes_view ( menu_array_layer values )
3896 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3897 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3899 if ( g_hash_table_size (vtl->routes) > 0 ) {
3900 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3901 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3902 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3903 vik_layers_panel_emit_update ( vlp );
3908 static void trw_layer_finish_track ( menu_array_layer values )
3910 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3911 vtl->current_track = NULL;
3912 vtl->route_finder_started = FALSE;
3913 vik_layer_emit_update ( VIK_LAYER(vtl) );
3916 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3918 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3919 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3921 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3922 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3923 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3924 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3925 vik_layers_panel_emit_update ( vlp );
3929 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3931 /* NB do not care if wp is visible or not */
3932 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3935 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3937 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3938 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3940 /* Only 1 waypoint - jump straight to it */
3941 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3942 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3943 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3945 /* If at least 2 waypoints - find center and then zoom to fit */
3946 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3948 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3949 maxmin[0].lat = vtl->waypoints_bbox.north;
3950 maxmin[1].lat = vtl->waypoints_bbox.south;
3951 maxmin[0].lon = vtl->waypoints_bbox.east;
3952 maxmin[1].lon = vtl->waypoints_bbox.west;
3953 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3956 vik_layers_panel_emit_update ( vlp );
3959 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3961 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3964 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3966 if ( values[MA_MISC] ) {
3967 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3968 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3972 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3974 static menu_array_layer pass_along;
3976 GtkWidget *export_submenu;
3977 pass_along[MA_VTL] = vtl;
3978 pass_along[MA_VLP] = vlp;
3980 item = gtk_menu_item_new();
3981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3982 gtk_widget_show ( item );
3984 if ( vtl->current_track ) {
3985 if ( vtl->current_track->is_route )
3986 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3988 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3990 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3991 gtk_widget_show ( item );
3994 item = gtk_menu_item_new ();
3995 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3996 gtk_widget_show ( item );
3999 /* Now with icons */
4000 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4003 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4004 gtk_widget_show ( item );
4006 GtkWidget *view_submenu = gtk_menu_new();
4007 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4010 gtk_widget_show ( item );
4011 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4013 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4015 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4016 gtk_widget_show ( item );
4018 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4020 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4021 gtk_widget_show ( item );
4023 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4025 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4026 gtk_widget_show ( item );
4028 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4031 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4032 gtk_widget_show ( item );
4034 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4036 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4037 gtk_widget_show ( item );
4039 export_submenu = gtk_menu_new ();
4040 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4042 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4043 gtk_widget_show ( item );
4044 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4046 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4048 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4049 gtk_widget_show ( item );
4051 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4053 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4054 gtk_widget_show ( item );
4056 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4058 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4059 gtk_widget_show ( item );
4061 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4063 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4064 gtk_widget_show ( item );
4066 if ( have_geojson_export ) {
4067 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4069 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4070 gtk_widget_show ( item );
4073 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4075 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4076 gtk_widget_show ( item );
4078 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4079 item = gtk_menu_item_new_with_mnemonic ( external1 );
4080 g_free ( external1 );
4081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4082 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4083 gtk_widget_show ( item );
4085 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4086 item = gtk_menu_item_new_with_mnemonic ( external2 );
4087 g_free ( external2 );
4088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4089 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4090 gtk_widget_show ( item );
4092 GtkWidget *new_submenu = gtk_menu_new();
4093 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4095 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4096 gtk_widget_show(item);
4097 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4099 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4102 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4103 gtk_widget_show ( item );
4105 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4108 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4109 gtk_widget_show ( item );
4110 // Make it available only when a new track *not* already in progress
4111 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4113 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4117 gtk_widget_show ( item );
4118 // Make it available only when a new track *not* already in progress
4119 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4121 #ifdef VIK_CONFIG_GEOTAG
4122 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4124 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4125 gtk_widget_show ( item );
4128 GtkWidget *acquire_submenu = gtk_menu_new ();
4129 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4131 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4132 gtk_widget_show ( item );
4133 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4135 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4137 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4138 gtk_widget_show ( item );
4140 /* FIXME: only add menu when at least a routing engine has support for Directions */
4141 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4144 gtk_widget_show ( item );
4146 #ifdef VIK_CONFIG_OPENSTREETMAP
4147 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4149 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4150 gtk_widget_show ( item );
4152 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4155 gtk_widget_show ( item );
4158 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4160 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4161 gtk_widget_show ( item );
4163 #ifdef VIK_CONFIG_GEONAMES
4164 GtkWidget *wikipedia_submenu = gtk_menu_new();
4165 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4167 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4168 gtk_widget_show(item);
4169 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4171 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4174 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4175 gtk_widget_show ( item );
4177 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4178 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4180 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4181 gtk_widget_show ( item );
4184 #ifdef VIK_CONFIG_GEOCACHES
4185 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4187 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4188 gtk_widget_show ( item );
4191 #ifdef VIK_CONFIG_GEOTAG
4192 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4193 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4194 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4195 gtk_widget_show ( item );
4198 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4200 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4201 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4202 gtk_widget_show ( item );
4204 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4206 GtkWidget *upload_submenu = gtk_menu_new ();
4207 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4209 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4210 gtk_widget_show ( item );
4211 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4216 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4217 gtk_widget_show ( item );
4219 #ifdef VIK_CONFIG_OPENSTREETMAP
4220 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4223 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4224 gtk_widget_show ( item );
4227 GtkWidget *delete_submenu = gtk_menu_new ();
4228 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4230 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4231 gtk_widget_show ( item );
4232 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4234 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4235 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4237 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4238 gtk_widget_show ( item );
4240 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4243 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4244 gtk_widget_show ( item );
4246 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4247 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4249 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4250 gtk_widget_show ( item );
4252 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4253 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4255 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4256 gtk_widget_show ( item );
4258 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4261 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4262 gtk_widget_show ( item );
4264 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4267 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4268 gtk_widget_show ( item );
4270 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4271 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4273 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4274 gtk_widget_show ( item );
4277 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4278 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4280 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4281 gtk_widget_show ( item );
4284 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4287 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4288 gtk_widget_show ( item );
4289 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4291 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4295 gtk_widget_show ( item );
4296 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4299 // Fake Waypoint UUIDs vi simple increasing integer
4300 static guint wp_uuid = 0;
4302 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4306 vik_waypoint_set_name (wp, name);
4308 if ( VIK_LAYER(vtl)->realized )
4310 // Do we need to create the sublayer:
4311 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4312 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4315 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4317 // Visibility column always needed for waypoints
4318 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
4320 // Actual setting of visibility dependent on the waypoint
4321 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4323 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4325 // Sort now as post_read is not called on a realized waypoint
4326 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4329 highest_wp_number_add_wp(vtl, name);
4330 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4334 // Fake Track UUIDs vi simple increasing integer
4335 static guint tr_uuid = 0;
4337 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4341 vik_track_set_name (t, name);
4343 if ( VIK_LAYER(vtl)->realized )
4345 // Do we need to create the sublayer:
4346 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4347 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4350 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4351 // Visibility column always needed for tracks
4352 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
4354 // Actual setting of visibility dependent on the track
4355 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4357 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4359 // Sort now as post_read is not called on a realized track
4360 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4363 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4365 trw_layer_update_treeview ( vtl, t );
4368 // Fake Route UUIDs vi simple increasing integer
4369 static guint rt_uuid = 0;
4371 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4375 vik_track_set_name (t, name);
4377 if ( VIK_LAYER(vtl)->realized )
4379 // Do we need to create the sublayer:
4380 if ( g_hash_table_size (vtl->routes) == 0 ) {
4381 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4384 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4385 // Visibility column always needed for routes
4386 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
4387 // Actual setting of visibility dependent on the route
4388 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4390 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4392 // Sort now as post_read is not called on a realized route
4393 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4396 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4398 trw_layer_update_treeview ( vtl, t );
4401 /* to be called whenever a track has been deleted or may have been changed. */
4402 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4404 if (vtl->current_tp_track == trk )
4405 trw_layer_cancel_current_tp ( vtl, FALSE );
4409 * Normally this is done to due the waypoint size preference having changed
4411 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4413 GHashTableIter iter;
4414 gpointer key, value;
4417 g_hash_table_iter_init ( &iter, vtl->waypoints );
4418 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4419 VikWaypoint *wp = VIK_WAYPOINT(value);
4421 // Reapply symbol setting to update the pixbuf
4422 gchar *tmp_symbol = g_strdup ( wp->symbol );
4423 vik_waypoint_set_symbol ( wp, tmp_symbol );
4424 g_free ( tmp_symbol );
4430 * trw_layer_new_unique_sublayer_name:
4432 * Allocates a unique new name
4434 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4437 gchar *newname = g_strdup(name);
4442 switch ( sublayer_type ) {
4443 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4444 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4446 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4447 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4450 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4453 // If found a name already in use try adding 1 to it and we try again
4455 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4457 newname = new_newname;
4460 } while ( id != NULL);
4465 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4467 // No more uniqueness of name forced when loading from a file
4468 // This now makes this function a little redunant as we just flow the parameters through
4469 vik_trw_layer_add_waypoint ( vtl, name, wp );
4472 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4474 if ( vtl->route_finder_append && vtl->current_track ) {
4475 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4477 // enforce end of current track equal to start of tr
4478 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4479 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4480 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4481 vik_track_add_trackpoint ( vtl->current_track,
4482 vik_trackpoint_copy ( cur_end ),
4486 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4487 vik_track_free ( tr );
4488 vtl->route_finder_append = FALSE; /* this means we have added it */
4491 // No more uniqueness of name forced when loading from a file
4493 vik_trw_layer_add_route ( vtl, name, tr );
4495 vik_trw_layer_add_track ( vtl, name, tr );
4497 if ( vtl->route_finder_check_added_track ) {
4498 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4499 vtl->route_finder_added_track = tr;
4504 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4506 *l = g_list_append(*l, id);
4510 * Move an item from one TRW layer to another TRW layer
4512 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4514 // TODO reconsider strategy when moving within layer (if anything...)
4515 gboolean rename = ( vtl_src != vtl_dest );
4519 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4520 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4524 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4526 newname = g_strdup ( trk->name );
4528 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4529 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4531 vik_trw_layer_delete_track ( vtl_src, trk );
4534 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4535 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4539 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4541 newname = g_strdup ( trk->name );
4543 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4544 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4546 vik_trw_layer_delete_route ( vtl_src, trk );
4549 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4550 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4554 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4556 newname = g_strdup ( wp->name );
4558 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4559 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4561 trw_layer_delete_waypoint ( vtl_src, wp );
4563 // Recalculate bounds even if not renamed as maybe dragged between layers
4564 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4565 trw_layer_calculate_bounds_waypoints ( vtl_src );
4569 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4571 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4572 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4574 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4575 GList *items = NULL;
4578 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4579 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4581 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4582 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4584 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4585 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4590 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4591 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4592 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4593 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4595 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4602 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4603 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4607 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4609 trku_udata *user_data = udata;
4610 if ( trk == user_data->trk ) {
4611 user_data->uuid = id;
4617 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4619 gboolean was_visible = FALSE;
4620 if ( trk && trk->name ) {
4622 if ( trk == vtl->current_track ) {
4623 vtl->current_track = NULL;
4624 vtl->current_tp_track = NULL;
4625 vtl->current_tp_id = NULL;
4626 vtl->moving_tp = FALSE;
4627 vtl->route_finder_started = FALSE;
4630 was_visible = trk->visible;
4632 if ( trk == vtl->route_finder_added_track )
4633 vtl->route_finder_added_track = NULL;
4639 // Hmmm, want key of it
4640 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4642 if ( trkf && udata.uuid ) {
4643 /* could be current_tp, so we have to check */
4644 trw_layer_cancel_tps_of_track ( vtl, trk );
4646 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4649 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4650 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4651 g_hash_table_remove ( vtl->tracks, udata.uuid );
4653 // If last sublayer, then remove sublayer container
4654 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4655 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4658 // Incase it was selected (no item delete signal ATM)
4659 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4665 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4667 gboolean was_visible = FALSE;
4669 if ( trk && trk->name ) {
4671 if ( trk == vtl->current_track ) {
4672 vtl->current_track = NULL;
4673 vtl->current_tp_track = NULL;
4674 vtl->current_tp_id = NULL;
4675 vtl->moving_tp = FALSE;
4678 was_visible = trk->visible;
4680 if ( trk == vtl->route_finder_added_track )
4681 vtl->route_finder_added_track = NULL;
4687 // Hmmm, want key of it
4688 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4690 if ( trkf && udata.uuid ) {
4691 /* could be current_tp, so we have to check */
4692 trw_layer_cancel_tps_of_track ( vtl, trk );
4694 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4697 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4698 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4699 g_hash_table_remove ( vtl->routes, udata.uuid );
4701 // If last sublayer, then remove sublayer container
4702 if ( g_hash_table_size (vtl->routes) == 0 ) {
4703 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4706 // Incase it was selected (no item delete signal ATM)
4707 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4713 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4715 gboolean was_visible = FALSE;
4717 if ( wp && wp->name ) {
4719 if ( wp == vtl->current_wp ) {
4720 vtl->current_wp = NULL;
4721 vtl->current_wp_id = NULL;
4722 vtl->moving_wp = FALSE;
4725 was_visible = wp->visible;
4731 // Hmmm, want key of it
4732 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4734 if ( wpf && udata.uuid ) {
4735 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4738 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4739 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4741 highest_wp_number_remove_wp(vtl, wp->name);
4742 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4744 // If last sublayer, then remove sublayer container
4745 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4746 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4749 // Incase it was selected (no item delete signal ATM)
4750 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4758 // Only for temporary use by trw_layer_delete_waypoint_by_name
4759 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4761 wpu_udata *user_data = udata;
4762 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4763 user_data->uuid = id;
4770 * Delete a waypoint by the given name
4771 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4772 * as there be multiple waypoints with the same name
4774 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4777 // Fake a waypoint with the given name
4778 udata.wp = vik_waypoint_new ();
4779 vik_waypoint_set_name (udata.wp, name);
4780 // Currently only the name is used in this waypoint find function
4783 // Hmmm, want key of it
4784 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4786 vik_waypoint_free (udata.wp);
4788 if ( wpf && udata.uuid )
4789 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4795 VikTrack *trk; // input
4796 gpointer uuid; // output
4799 // Only for temporary use by trw_layer_delete_track_by_name
4800 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4802 tpu_udata *user_data = udata;
4803 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4804 user_data->uuid = id;
4811 * Delete a track by the given name
4812 * NOTE: ATM this will delete the first encountered Track with the specified name
4813 * as there may be multiple tracks with the same name within the specified hash table
4815 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4818 // Fake a track with the given name
4819 udata.trk = vik_track_new ();
4820 vik_track_set_name (udata.trk, name);
4821 // Currently only the name is used in this waypoint find function
4824 // Hmmm, want key of it
4825 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4827 vik_track_free (udata.trk);
4829 if ( trkf && udata.uuid ) {
4830 // This could be a little better written...
4831 if ( vtl->tracks == ht_tracks )
4832 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4833 if ( vtl->routes == ht_tracks )
4834 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4841 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4843 vik_treeview_item_delete (vt, it );
4846 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4849 vtl->current_track = NULL;
4850 vtl->route_finder_added_track = NULL;
4851 if (vtl->current_tp_track)
4852 trw_layer_cancel_current_tp(vtl, FALSE);
4854 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4855 g_hash_table_remove_all(vtl->routes_iters);
4856 g_hash_table_remove_all(vtl->routes);
4858 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4860 vik_layer_emit_update ( VIK_LAYER(vtl) );
4863 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4866 vtl->current_track = NULL;
4867 vtl->route_finder_added_track = NULL;
4868 if (vtl->current_tp_track)
4869 trw_layer_cancel_current_tp(vtl, FALSE);
4871 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4872 g_hash_table_remove_all(vtl->tracks_iters);
4873 g_hash_table_remove_all(vtl->tracks);
4875 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4877 vik_layer_emit_update ( VIK_LAYER(vtl) );
4880 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4882 vtl->current_wp = NULL;
4883 vtl->current_wp_id = NULL;
4884 vtl->moving_wp = FALSE;
4886 highest_wp_number_reset(vtl);
4888 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4889 g_hash_table_remove_all(vtl->waypoints_iters);
4890 g_hash_table_remove_all(vtl->waypoints);
4892 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4894 vik_layer_emit_update ( VIK_LAYER(vtl) );
4897 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4899 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4900 // Get confirmation from the user
4901 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4902 _("Are you sure you want to delete all tracks in %s?"),
4903 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4904 vik_trw_layer_delete_all_tracks (vtl);
4907 static void trw_layer_delete_all_routes ( menu_array_layer values )
4909 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4910 // Get confirmation from the user
4911 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4912 _("Are you sure you want to delete all routes in %s?"),
4913 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4914 vik_trw_layer_delete_all_routes (vtl);
4917 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4919 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4920 // Get confirmation from the user
4921 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4922 _("Are you sure you want to delete all waypoints in %s?"),
4923 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4924 vik_trw_layer_delete_all_waypoints (vtl);
4927 static void trw_layer_delete_item ( menu_array_sublayer values )
4929 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4930 gboolean was_visible = FALSE;
4931 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4933 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4934 if ( wp && wp->name ) {
4935 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4936 // Get confirmation from the user
4937 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4938 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4939 _("Are you sure you want to delete the waypoint \"%s\"?"),
4942 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4943 trw_layer_calculate_bounds_waypoints ( vtl );
4946 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4948 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4949 if ( trk && trk->name ) {
4950 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4951 // Get confirmation from the user
4952 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4953 _("Are you sure you want to delete the track \"%s\"?"),
4956 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4961 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4962 if ( trk && trk->name ) {
4963 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4964 // Get confirmation from the user
4965 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4966 _("Are you sure you want to delete the route \"%s\"?"),
4969 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4973 vik_layer_emit_update ( VIK_LAYER(vtl) );
4977 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4979 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4981 vik_waypoint_set_name ( wp, new_name );
4983 // Now update the treeview as well
4988 // Need key of it for treeview update
4989 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4991 if ( wpf && udataU.uuid ) {
4992 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4995 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4996 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
5002 * Maintain icon of waypoint in the treeview
5004 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5006 // update the treeview
5011 // Need key of it for treeview update
5012 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5014 if ( wpf && udataU.uuid ) {
5015 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5018 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5023 static void trw_layer_properties_item ( menu_array_sublayer values )
5025 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5026 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5028 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5030 if ( wp && wp->name )
5032 gboolean updated = FALSE;
5033 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5035 trw_layer_waypoint_rename ( vtl, wp, new_name );
5037 if ( updated && values[MA_TV_ITER] )
5038 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5040 if ( updated && VIK_LAYER(vtl)->visible )
5041 vik_layer_emit_update ( VIK_LAYER(vtl) );
5047 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5048 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5050 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5052 if ( tr && tr->name )
5054 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5065 * trw_layer_track_statistics:
5067 * Show track statistics.
5068 * ATM jump to the stats page in the properties
5069 * TODO: consider separating the stats into an individual dialog?
5071 static void trw_layer_track_statistics ( menu_array_sublayer values )
5073 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5075 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5076 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5078 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5080 if ( trk && trk->name ) {
5081 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5091 * Update the treeview of the track id - primarily to update the icon
5093 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5099 gpointer *trkf = NULL;
5100 if ( trk->is_route )
5101 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5103 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5105 if ( trkf && udata.uuid ) {
5107 GtkTreeIter *iter = NULL;
5108 if ( trk->is_route )
5109 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5111 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5114 // TODO: Make this a function
5115 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5116 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5117 ((trk->color.green & 0xff00) << 8) |
5118 (trk->color.blue & 0xff00);
5119 gdk_pixbuf_fill ( pixbuf, pixel );
5120 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5121 g_object_unref (pixbuf);
5128 Parameter 1 -> VikLayersPanel
5129 Parameter 2 -> VikLayer
5130 Parameter 3 -> VikViewport
5132 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5135 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5136 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5139 /* since vlp not set, vl & vvp should be valid instead! */
5141 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5142 vik_layer_emit_update ( VIK_LAYER(vl) );
5147 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5149 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5151 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5152 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5154 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5156 if ( track && track->trackpoints )
5157 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5160 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5162 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5164 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5165 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5167 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5169 if ( track && track->trackpoints )
5171 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5173 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5174 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5175 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5176 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5177 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5181 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5183 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5185 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5186 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5188 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5193 // Converting a track to a route can be a bit more complicated,
5194 // so give a chance to change our minds:
5195 if ( !trk->is_route &&
5196 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5197 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5199 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5200 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5205 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5208 trk_copy->is_route = !trk_copy->is_route;
5210 // ATM can't set name to self - so must create temporary copy
5211 gchar *name = g_strdup ( trk_copy->name );
5213 // Delete old one and then add new one
5214 if ( trk->is_route ) {
5215 vik_trw_layer_delete_route ( vtl, trk );
5216 vik_trw_layer_add_track ( vtl, name, trk_copy );
5219 // Extra route conversion bits...
5220 vik_track_merge_segments ( trk_copy );
5221 vik_track_to_routepoints ( trk_copy );
5223 vik_trw_layer_delete_track ( vtl, trk );
5224 vik_trw_layer_add_route ( vtl, name, trk_copy );
5228 // Update in case color of track / route changes when moving between sublayers
5229 vik_layer_emit_update ( VIK_LAYER(vtl) );
5232 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5234 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5236 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5237 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5239 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5242 vik_track_anonymize_times ( track );
5245 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5247 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5249 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5250 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5252 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5257 vtl->current_track = track;
5258 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
5260 if ( track->trackpoints )
5261 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5265 * extend a track using route finder
5267 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5269 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5270 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5274 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5275 vtl->current_track = track;
5276 vtl->route_finder_started = TRUE;
5278 if ( track->trackpoints )
5279 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5285 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5287 // If have a vlp then perform a basic test to see if any DEM info available...
5289 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5291 if ( !g_list_length(dems) ) {
5292 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5300 * apply_dem_data_common:
5302 * A common function for applying the DEM values and reporting the results.
5304 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5306 if ( !trw_layer_dem_test ( vtl, vlp ) )
5309 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5310 // Inform user how much was changed
5312 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5313 g_snprintf(str, 64, tmp_str, changed);
5314 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5317 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5319 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5321 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5322 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5324 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5327 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5330 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5332 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5334 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5335 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5337 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5340 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5346 * A common function for applying the elevation smoothing and reporting the results.
5348 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5350 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5351 // Inform user how much was changed
5353 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5354 g_snprintf(str, 64, tmp_str, changed);
5355 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5361 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5363 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5365 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5366 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5368 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5373 smooth_it ( vtl, track, FALSE );
5376 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5378 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5380 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5381 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5383 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5388 smooth_it ( vtl, track, TRUE );
5392 * Commonal helper function
5394 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5397 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5398 g_snprintf(str, 64, tmp_str, changed);
5399 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5402 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5404 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5405 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5407 if ( !trw_layer_dem_test ( vtl, vlp ) )
5411 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5413 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5415 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5419 GHashTableIter iter;
5420 gpointer key, value;
5422 g_hash_table_iter_init ( &iter, vtl->waypoints );
5423 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5424 VikWaypoint *wp = VIK_WAYPOINT(value);
5425 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5428 wp_changed_message ( vtl, changed );
5431 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5433 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5434 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5436 if ( !trw_layer_dem_test ( vtl, vlp ) )
5440 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5442 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5444 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5448 GHashTableIter iter;
5449 gpointer key, value;
5451 g_hash_table_iter_init ( &iter, vtl->waypoints );
5452 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5453 VikWaypoint *wp = VIK_WAYPOINT(value);
5454 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5457 wp_changed_message ( vtl, changed );
5460 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5462 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5464 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5471 if ( !track->trackpoints )
5473 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5476 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5478 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5480 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5481 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5483 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5488 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5491 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5494 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5496 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5498 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5499 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5501 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5506 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5509 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5512 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5514 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5516 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5517 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5519 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5524 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5527 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5531 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5533 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5535 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5537 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5538 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5540 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5542 if ( trk && trk->trackpoints )
5544 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5545 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5546 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5547 if ( values[MA_VLP] )
5548 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5550 vik_layer_emit_update ( VIK_LAYER(vtl) );
5555 * Refine the selected track/route with a routing engine.
5556 * The routing engine is selected by the user, when requestiong the job.
5558 static void trw_layer_route_refine ( menu_array_sublayer values )
5560 static gint last_engine = 0;
5561 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5564 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5565 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5567 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5569 if ( trk && trk->trackpoints )
5571 /* Check size of the route */
5572 int nb = vik_track_get_tp_count(trk);
5574 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5575 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5576 GTK_MESSAGE_WARNING,
5577 GTK_BUTTONS_OK_CANCEL,
5578 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5580 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5581 gtk_widget_destroy ( dialog );
5582 if (response != GTK_RESPONSE_OK )
5585 /* Select engine from dialog */
5586 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5587 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5588 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5590 GTK_RESPONSE_REJECT,
5592 GTK_RESPONSE_ACCEPT,
5594 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5595 gtk_widget_show_all(label);
5597 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5599 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5600 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5601 gtk_widget_show_all(combo);
5603 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5605 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5607 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5609 /* Dialog validated: retrieve selected engine and do the job */
5610 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5611 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5614 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5616 /* Force saving track */
5617 /* FIXME: remove or rename this hack */
5618 vtl->route_finder_check_added_track = TRUE;
5621 vik_routing_engine_refine (routing, vtl, trk);
5623 /* FIXME: remove or rename this hack */
5624 if ( vtl->route_finder_added_track )
5625 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5627 vtl->route_finder_added_track = NULL;
5628 vtl->route_finder_check_added_track = FALSE;
5630 vik_layer_emit_update ( VIK_LAYER(vtl) );
5632 /* Restore cursor */
5633 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5635 gtk_widget_destroy ( dialog );
5639 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5641 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5642 trw_layer_tpwin_init ( vtl );
5645 /*************************************
5646 * merge/split by time routines
5647 *************************************/
5649 /* called for each key in track hash table.
5650 * If the current track has the same time stamp type, add it to the result,
5651 * except the one pointed by "exclude".
5652 * set exclude to NULL if there is no exclude to check.
5653 * Note that the result is in reverse (for performance reasons).
5658 gboolean with_timestamps;
5660 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5662 twt_udata *user_data = udata;
5663 VikTrackpoint *p1, *p2;
5664 VikTrack *trk = VIK_TRACK(value);
5665 if (trk == user_data->exclude) {
5669 if (trk->trackpoints) {
5670 p1 = vik_track_get_tp_first(trk);
5671 p2 = vik_track_get_tp_last(trk);
5673 if ( user_data->with_timestamps ) {
5674 if (!p1->has_timestamp || !p2->has_timestamp) {
5679 // Don't add tracks with timestamps when getting non timestamp tracks
5680 if (p1->has_timestamp || p2->has_timestamp) {
5686 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5690 * find_nearby_tracks_by_time:
5692 * Called for each track in track hash table.
5693 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5694 * to the current track, then the current track is added to the list in user_data[0]
5696 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5698 VikTrack *trk = VIK_TRACK(value);
5700 GList **nearby_tracks = ((gpointer *)user_data)[0];
5701 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5703 if ( !orig_trk || !orig_trk->trackpoints )
5707 * detect reasons for not merging, and return
5708 * if no reason is found not to merge, then do it.
5711 twt_udata *udata = user_data;
5712 // Exclude the original track from the compiled list
5713 if (trk == udata->exclude) {
5717 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5718 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5720 if (trk->trackpoints) {
5722 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5723 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5725 if (!p1->has_timestamp || !p2->has_timestamp) {
5726 //g_print("no timestamp\n");
5730 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5731 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5732 if (! (abs(t1 - p2->timestamp) < threshold ||
5734 abs(p1->timestamp - t2) < threshold)
5741 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5744 /* comparison function used to sort tracks; a and b are hash table keys */
5745 /* Not actively used - can be restored if needed
5746 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5748 GHashTable *tracks = user_data;
5751 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5752 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5754 if (t1 < t2) return -1;
5755 if (t1 > t2) return 1;
5760 /* comparison function used to sort trackpoints */
5761 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5763 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5765 if (t1 < t2) return -1;
5766 if (t1 > t2) return 1;
5771 * comparison function which can be used to sort tracks or waypoints by name
5773 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5775 const gchar* namea = (const gchar*) a;
5776 const gchar* nameb = (const gchar*) b;
5777 if ( namea == NULL || nameb == NULL)
5780 // Same sort method as used in the vik_treeview_*_alphabetize functions
5781 return strcmp ( namea, nameb );
5785 * Attempt to merge selected track with other tracks specified by the user
5786 * Tracks to merge with must be of the same 'type' as the selected track -
5787 * either all with timestamps, or all without timestamps
5789 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5791 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5792 GList *other_tracks = NULL;
5793 GHashTable *ght_tracks;
5794 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5795 ght_tracks = vtl->routes;
5797 ght_tracks = vtl->tracks;
5799 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5804 if ( !track->trackpoints )
5808 udata.result = &other_tracks;
5809 udata.exclude = track;
5810 // Allow merging with 'similar' time type time tracks
5811 // i.e. either those times, or those without
5812 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5814 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5815 other_tracks = g_list_reverse(other_tracks);
5817 if ( !other_tracks ) {
5818 if ( udata.with_timestamps )
5819 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5821 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5825 // Sort alphabetically for user presentation
5826 // Convert into list of names for usage with dialog function
5827 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5828 GList *other_tracks_names = NULL;
5829 GList *iter = g_list_first ( other_tracks );
5831 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5832 iter = g_list_next ( iter );
5835 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5837 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5841 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5842 g_list_free(other_tracks);
5843 g_list_free(other_tracks_names);
5848 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5849 VikTrack *merge_track;
5850 if ( track->is_route )
5851 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5853 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5856 vik_track_steal_and_append_trackpoints ( track, merge_track );
5857 if ( track->is_route )
5858 vik_trw_layer_delete_route (vtl, merge_track);
5860 vik_trw_layer_delete_track (vtl, merge_track);
5861 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5864 for (l = merge_list; l != NULL; l = g_list_next(l))
5866 g_list_free(merge_list);
5868 vik_layer_emit_update( VIK_LAYER(vtl) );
5872 // c.f. trw_layer_sorted_track_id_by_name_list
5873 // but don't add the specified track to the list (normally current track)
5874 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5876 twt_udata *user_data = udata;
5879 if (trk == user_data->exclude) {
5883 // Sort named list alphabetically
5884 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5888 * Join - this allows combining 'tracks' and 'track routes'
5889 * i.e. doesn't care about whether tracks have consistent timestamps
5890 * ATM can only append one track at a time to the currently selected track
5892 static void trw_layer_append_track ( menu_array_sublayer values )
5895 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5897 GHashTable *ght_tracks;
5898 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5899 ght_tracks = vtl->routes;
5901 ght_tracks = vtl->tracks;
5903 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5908 GList *other_tracks_names = NULL;
5910 // Sort alphabetically for user presentation
5911 // Convert into list of names for usage with dialog function
5912 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5914 udata.result = &other_tracks_names;
5915 udata.exclude = trk;
5917 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5919 // Note the limit to selecting one track only
5920 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5921 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5922 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5925 trk->is_route ? _("Append Route"): _("Append Track"),
5926 trk->is_route ? _("Select the route to append after the current route") :
5927 _("Select the track to append after the current track") );
5929 g_list_free(other_tracks_names);
5931 // It's a list, but shouldn't contain more than one other track!
5932 if ( append_list ) {
5934 for (l = append_list; l != NULL; l = g_list_next(l)) {
5935 // TODO: at present this uses the first track found by name,
5936 // which with potential multiple same named tracks may not be the one selected...
5937 VikTrack *append_track;
5938 if ( trk->is_route )
5939 append_track = vik_trw_layer_get_route ( vtl, l->data );
5941 append_track = vik_trw_layer_get_track ( vtl, l->data );
5943 if ( append_track ) {
5944 vik_track_steal_and_append_trackpoints ( trk, append_track );
5945 if ( trk->is_route )
5946 vik_trw_layer_delete_route (vtl, append_track);
5948 vik_trw_layer_delete_track (vtl, append_track);
5951 for (l = append_list; l != NULL; l = g_list_next(l))
5953 g_list_free(append_list);
5955 vik_layer_emit_update( VIK_LAYER(vtl) );
5960 * Very similar to trw_layer_append_track for joining
5961 * but this allows selection from the 'other' list
5962 * If a track is selected, then is shows routes and joins the selected one
5963 * If a route is selected, then is shows tracks and joins the selected one
5965 static void trw_layer_append_other ( menu_array_sublayer values )
5968 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5970 GHashTable *ght_mykind, *ght_others;
5971 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5972 ght_mykind = vtl->routes;
5973 ght_others = vtl->tracks;
5976 ght_mykind = vtl->tracks;
5977 ght_others = vtl->routes;
5980 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5985 GList *other_tracks_names = NULL;
5987 // Sort alphabetically for user presentation
5988 // Convert into list of names for usage with dialog function
5989 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5991 udata.result = &other_tracks_names;
5992 udata.exclude = trk;
5994 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5996 // Note the limit to selecting one track only
5997 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5998 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5999 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6002 trk->is_route ? _("Append Track"): _("Append Route"),
6003 trk->is_route ? _("Select the track to append after the current route") :
6004 _("Select the route to append after the current track") );
6006 g_list_free(other_tracks_names);
6008 // It's a list, but shouldn't contain more than one other track!
6009 if ( append_list ) {
6011 for (l = append_list; l != NULL; l = g_list_next(l)) {
6012 // TODO: at present this uses the first track found by name,
6013 // which with potential multiple same named tracks may not be the one selected...
6015 // Get FROM THE OTHER TYPE list
6016 VikTrack *append_track;
6017 if ( trk->is_route )
6018 append_track = vik_trw_layer_get_track ( vtl, l->data );
6020 append_track = vik_trw_layer_get_route ( vtl, l->data );
6022 if ( append_track ) {
6024 if ( !append_track->is_route &&
6025 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6026 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6028 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6029 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6030 vik_track_merge_segments ( append_track );
6031 vik_track_to_routepoints ( append_track );
6038 vik_track_steal_and_append_trackpoints ( trk, append_track );
6040 // Delete copied which is FROM THE OTHER TYPE list
6041 if ( trk->is_route )
6042 vik_trw_layer_delete_track (vtl, append_track);
6044 vik_trw_layer_delete_route (vtl, append_track);
6047 for (l = append_list; l != NULL; l = g_list_next(l))
6049 g_list_free(append_list);
6050 vik_layer_emit_update( VIK_LAYER(vtl) );
6054 /* merge by segments */
6055 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6057 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6058 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6059 guint segments = vik_track_merge_segments ( trk );
6060 // NB currently no need to redraw as segments not actually shown on the display
6061 // However inform the user of what happened:
6063 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6064 g_snprintf(str, 64, tmp_str, segments);
6065 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6068 /* merge by time routine */
6069 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6071 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6075 GList *tracks_with_timestamp = NULL;
6076 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6077 if (orig_trk->trackpoints &&
6078 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6079 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6084 udata.result = &tracks_with_timestamp;
6085 udata.exclude = orig_trk;
6086 udata.with_timestamps = TRUE;
6087 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6088 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6090 if (!tracks_with_timestamp) {
6091 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6094 g_list_free(tracks_with_timestamp);
6096 static guint threshold_in_minutes = 1;
6097 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6098 _("Merge Threshold..."),
6099 _("Merge when time between tracks less than:"),
6100 &threshold_in_minutes)) {
6104 // keep attempting to merge all tracks until no merges within the time specified is possible
6105 gboolean attempt_merge = TRUE;
6106 GList *nearby_tracks = NULL;
6108 static gpointer params[3];
6110 while ( attempt_merge ) {
6112 // Don't try again unless tracks have changed
6113 attempt_merge = FALSE;
6115 trps = orig_trk->trackpoints;
6119 if (nearby_tracks) {
6120 g_list_free(nearby_tracks);
6121 nearby_tracks = NULL;
6124 params[0] = &nearby_tracks;
6125 params[1] = orig_trk;
6126 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6128 /* get a list of adjacent-in-time tracks */
6129 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6132 GList *l = nearby_tracks;
6134 /* remove trackpoints from merged track, delete track */
6135 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6136 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6138 // Tracks have changed, therefore retry again against all the remaining tracks
6139 attempt_merge = TRUE;
6144 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6147 g_list_free(nearby_tracks);
6149 vik_layer_emit_update( VIK_LAYER(vtl) );
6153 * Split a track at the currently selected trackpoint
6155 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6157 if ( !vtl->current_tpl )
6160 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6161 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6163 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6164 GList *newglist = g_list_alloc ();
6165 newglist->prev = NULL;
6166 newglist->next = vtl->current_tpl->next;
6167 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6168 tr->trackpoints = newglist;
6170 vtl->current_tpl->next->prev = newglist; /* end old track here */
6171 vtl->current_tpl->next = NULL;
6173 // Bounds of the selected track changed due to the split
6174 vik_track_calculate_bounds ( vtl->current_tp_track );
6176 vtl->current_tpl = newglist; /* change tp to first of new track. */
6177 vtl->current_tp_track = tr;
6180 vik_trw_layer_add_route ( vtl, name, tr );
6182 vik_trw_layer_add_track ( vtl, name, tr );
6184 // Bounds of the new track created by the split
6185 vik_track_calculate_bounds ( tr );
6191 // Also need id of newly created track
6194 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6196 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6198 if ( trkf && udata.uuid )
6199 vtl->current_tp_id = udata.uuid;
6201 vtl->current_tp_id = NULL;
6203 vik_layer_emit_update(VIK_LAYER(vtl));
6209 /* split by time routine */
6210 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6212 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6213 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6214 GList *trps = track->trackpoints;
6216 GList *newlists = NULL;
6217 GList *newtps = NULL;
6218 static guint thr = 1;
6225 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6226 _("Split Threshold..."),
6227 _("Split when time between trackpoints exceeds:"),
6232 /* iterate through trackpoints, and copy them into new lists without touching original list */
6233 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6237 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6239 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6242 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6243 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6244 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6246 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6251 if (ts - prev_ts > thr*60) {
6252 /* flush accumulated trackpoints into new list */
6253 newlists = g_list_append(newlists, g_list_reverse(newtps));
6257 /* accumulate trackpoint copies in newtps, in reverse order */
6258 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6260 iter = g_list_next(iter);
6263 newlists = g_list_append(newlists, g_list_reverse(newtps));
6266 /* put lists of trackpoints into tracks */
6268 // Only bother updating if the split results in new tracks
6269 if (g_list_length (newlists) > 1) {
6274 tr = vik_track_copy ( track, FALSE );
6275 tr->trackpoints = (GList *)(iter->data);
6277 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6278 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6279 g_free ( new_tr_name );
6280 vik_track_calculate_bounds ( tr );
6281 iter = g_list_next(iter);
6283 // Remove original track and then update the display
6284 vik_trw_layer_delete_track (vtl, track);
6285 vik_layer_emit_update(VIK_LAYER(vtl));
6287 g_list_free(newlists);
6291 * Split a track by the number of points as specified by the user
6293 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6295 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6297 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6298 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6300 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6305 // Check valid track
6306 GList *trps = track->trackpoints;
6310 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6311 _("Split Every Nth Point"),
6312 _("Split on every Nth point:"),
6313 250, // Default value as per typical limited track capacity of various GPS devices
6317 // Was a valid number returned?
6323 GList *newlists = NULL;
6324 GList *newtps = NULL;
6329 /* accumulate trackpoint copies in newtps, in reverse order */
6330 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6332 if (count >= points) {
6333 /* flush accumulated trackpoints into new list */
6334 newlists = g_list_append(newlists, g_list_reverse(newtps));
6338 iter = g_list_next(iter);
6341 // If there is a remaining chunk put that into the new split list
6342 // This may well be the whole track if no split points were encountered
6344 newlists = g_list_append(newlists, g_list_reverse(newtps));
6347 /* put lists of trackpoints into tracks */
6349 // Only bother updating if the split results in new tracks
6350 if (g_list_length (newlists) > 1) {
6355 tr = vik_track_copy ( track, FALSE );
6356 tr->trackpoints = (GList *)(iter->data);
6358 if ( track->is_route ) {
6359 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6360 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6363 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6364 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6366 g_free ( new_tr_name );
6367 vik_track_calculate_bounds ( tr );
6369 iter = g_list_next(iter);
6371 // Remove original track and then update the display
6372 if ( track->is_route )
6373 vik_trw_layer_delete_route (vtl, track);
6375 vik_trw_layer_delete_track (vtl, track);
6376 vik_layer_emit_update(VIK_LAYER(vtl));
6378 g_list_free(newlists);
6382 * Split a track at the currently selected trackpoint
6384 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6386 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6387 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6388 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6392 * Split a track by its segments
6393 * Routes do not have segments so don't call this for routes
6395 static void trw_layer_split_segments ( menu_array_sublayer values )
6397 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6398 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6405 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6408 for ( i = 0; i < ntracks; i++ ) {
6410 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6411 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6412 g_free ( new_tr_name );
6417 // Remove original track
6418 vik_trw_layer_delete_track ( vtl, trk );
6419 vik_layer_emit_update ( VIK_LAYER(vtl) );
6422 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6425 /* end of split/merge routines */
6427 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6431 // Find available adjacent trackpoint
6432 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6433 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6434 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6436 // Delete current trackpoint
6437 vik_trackpoint_free ( vtl->current_tpl->data );
6438 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6440 // Set to current to the available adjacent trackpoint
6441 vtl->current_tpl = new_tpl;
6443 if ( vtl->current_tp_track ) {
6444 vik_track_calculate_bounds ( vtl->current_tp_track );
6448 // Delete current trackpoint
6449 vik_trackpoint_free ( vtl->current_tpl->data );
6450 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6451 trw_layer_cancel_current_tp ( vtl, FALSE );
6456 * Delete the selected point
6458 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6460 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6462 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6463 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6465 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6470 if ( !vtl->current_tpl )
6473 trw_layer_trackpoint_selected_delete ( vtl, trk );
6475 // Track has been updated so update tps:
6476 trw_layer_cancel_tps_of_track ( vtl, trk );
6478 vik_layer_emit_update ( VIK_LAYER(vtl) );
6482 * Delete adjacent track points at the same position
6483 * AKA Delete Dulplicates on the Properties Window
6485 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6487 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6489 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6490 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6492 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6497 gulong removed = vik_track_remove_dup_points ( trk );
6499 // Track has been updated so update tps:
6500 trw_layer_cancel_tps_of_track ( vtl, trk );
6502 // Inform user how much was deleted as it's not obvious from the normal view
6504 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6505 g_snprintf(str, 64, tmp_str, removed);
6506 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6508 vik_layer_emit_update ( VIK_LAYER(vtl) );
6512 * Delete adjacent track points with the same timestamp
6513 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6515 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6517 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6519 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6520 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6522 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6527 gulong removed = vik_track_remove_same_time_points ( trk );
6529 // Track has been updated so update tps:
6530 trw_layer_cancel_tps_of_track ( vtl, trk );
6532 // Inform user how much was deleted as it's not obvious from the normal view
6534 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6535 g_snprintf(str, 64, tmp_str, removed);
6536 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6538 vik_layer_emit_update ( VIK_LAYER(vtl) );
6544 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6546 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6548 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6549 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6551 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6556 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6558 vik_layer_emit_update ( VIK_LAYER(vtl) );
6561 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6563 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6565 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6566 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6568 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6573 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6575 vik_layer_emit_update ( VIK_LAYER(vtl) );
6581 static void trw_layer_reverse ( menu_array_sublayer values )
6583 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6585 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6586 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6588 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6593 vik_track_reverse ( track );
6595 vik_layer_emit_update ( VIK_LAYER(vtl) );
6599 * Open a diary at the specified date
6601 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6604 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6605 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6606 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6607 g_error_free ( err );
6613 * Open a diary at the date of the track or waypoint
6615 static void trw_layer_diary ( menu_array_sublayer values )
6617 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6619 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6620 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6626 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6627 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6628 trw_layer_diary_open ( vtl, date_buf );
6631 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6633 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6634 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6640 if ( wpt->has_timestamp ) {
6641 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6642 trw_layer_diary_open ( vtl, date_buf );
6645 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6650 * Similar to trw_layer_enum_item, but this uses a sorted method
6653 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6655 GList **list = (GList**)udata;
6656 // *list = g_list_prepend(*all, key); //unsorted method
6657 // Sort named list alphabetically
6658 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6663 * Now Waypoint specific sort
6665 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6667 GList **list = (GList**)udata;
6668 // Sort named list alphabetically
6669 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6673 * Track specific sort
6675 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6677 GList **list = (GList**)udata;
6678 // Sort named list alphabetically
6679 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6684 gboolean has_same_track_name;
6685 const gchar *same_track_name;
6686 } same_track_name_udata;
6688 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6690 const gchar* namea = (const gchar*) aa;
6691 const gchar* nameb = (const gchar*) bb;
6694 gint result = strcmp ( namea, nameb );
6696 if ( result == 0 ) {
6697 // Found two names the same
6698 same_track_name_udata *user_data = udata;
6699 user_data->has_same_track_name = TRUE;
6700 user_data->same_track_name = namea;
6703 // Leave ordering the same
6708 * Find out if any tracks have the same name in this hash table
6710 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6712 // Sort items by name, then compare if any next to each other are the same
6714 GList *track_names = NULL;
6715 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6718 if ( ! track_names )
6721 same_track_name_udata udata;
6722 udata.has_same_track_name = FALSE;
6724 // Use sort routine to traverse list comparing items
6725 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6726 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6727 // Still no tracks...
6731 return udata.has_same_track_name;
6735 * Force unqiue track names for the track table specified
6736 * Note the panel is a required parameter to enable the update of the names displayed
6737 * Specify if on tracks or else on routes
6739 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6741 // . Search list for an instance of repeated name
6742 // . get track of this name
6743 // . create new name
6744 // . rename track & update equiv. treeview iter
6745 // . repeat until all different
6747 same_track_name_udata udata;
6749 GList *track_names = NULL;
6750 udata.has_same_track_name = FALSE;
6751 udata.same_track_name = NULL;
6753 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6756 if ( ! track_names )
6759 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6761 // Still no tracks...
6762 if ( ! dummy_list1 )
6765 while ( udata.has_same_track_name ) {
6767 // Find a track with the same name
6770 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6772 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6776 g_critical("Houston, we've had a problem.");
6777 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6778 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6783 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6784 vik_track_set_name ( trk, newname );
6790 // Need want key of it for treeview update
6791 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6793 if ( trkf && udataU.uuid ) {
6797 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6799 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6802 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6804 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6806 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6810 // Start trying to find same names again...
6812 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6813 udata.has_same_track_name = FALSE;
6814 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6816 // No tracks any more - give up searching
6817 if ( ! dummy_list2 )
6818 udata.has_same_track_name = FALSE;
6822 vik_layers_panel_emit_update ( vlp );
6825 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6827 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6830 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6831 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6832 iter = &(vtl->tracks_iter);
6833 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6835 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6836 iter = &(vtl->routes_iter);
6837 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6839 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6840 iter = &(vtl->waypoints_iter);
6841 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6845 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6848 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6850 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6853 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6854 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6855 iter = &(vtl->tracks_iter);
6856 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6858 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6859 iter = &(vtl->routes_iter);
6860 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6862 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6863 iter = &(vtl->waypoints_iter);
6864 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6868 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6874 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6876 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6879 // Ensure list of track names offered is unique
6880 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6881 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6882 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6883 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6889 // Sort list alphabetically for better presentation
6890 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6893 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6897 // Get list of items to delete from the user
6898 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6901 _("Delete Selection"),
6902 _("Select tracks to delete"));
6905 // Delete requested tracks
6906 // since specificly requested, IMHO no need for extra confirmation
6907 if ( delete_list ) {
6909 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6910 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6911 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6913 g_list_free(delete_list);
6914 vik_layer_emit_update( VIK_LAYER(vtl) );
6921 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6923 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6926 // Ensure list of track names offered is unique
6927 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6928 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6929 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6930 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6936 // Sort list alphabetically for better presentation
6937 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6940 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6944 // Get list of items to delete from the user
6945 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6948 _("Delete Selection"),
6949 _("Select routes to delete") );
6952 // Delete requested routes
6953 // since specificly requested, IMHO no need for extra confirmation
6954 if ( delete_list ) {
6956 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6957 // This deletes first route it finds of that name (but uniqueness is enforced above)
6958 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6960 g_list_free(delete_list);
6961 vik_layer_emit_update( VIK_LAYER(vtl) );
6966 gboolean has_same_waypoint_name;
6967 const gchar *same_waypoint_name;
6968 } same_waypoint_name_udata;
6970 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6972 const gchar* namea = (const gchar*) aa;
6973 const gchar* nameb = (const gchar*) bb;
6976 gint result = strcmp ( namea, nameb );
6978 if ( result == 0 ) {
6979 // Found two names the same
6980 same_waypoint_name_udata *user_data = udata;
6981 user_data->has_same_waypoint_name = TRUE;
6982 user_data->same_waypoint_name = namea;
6985 // Leave ordering the same
6990 * Find out if any waypoints have the same name in this layer
6992 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6994 // Sort items by name, then compare if any next to each other are the same
6996 GList *waypoint_names = NULL;
6997 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7000 if ( ! waypoint_names )
7003 same_waypoint_name_udata udata;
7004 udata.has_same_waypoint_name = FALSE;
7006 // Use sort routine to traverse list comparing items
7007 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7008 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7009 // Still no waypoints...
7013 return udata.has_same_waypoint_name;
7017 * Force unqiue waypoint names for this layer
7018 * Note the panel is a required parameter to enable the update of the names displayed
7020 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7022 // . Search list for an instance of repeated name
7023 // . get waypoint of this name
7024 // . create new name
7025 // . rename waypoint & update equiv. treeview iter
7026 // . repeat until all different
7028 same_waypoint_name_udata udata;
7030 GList *waypoint_names = NULL;
7031 udata.has_same_waypoint_name = FALSE;
7032 udata.same_waypoint_name = NULL;
7034 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7037 if ( ! waypoint_names )
7040 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7042 // Still no waypoints...
7043 if ( ! dummy_list1 )
7046 while ( udata.has_same_waypoint_name ) {
7048 // Find a waypoint with the same name
7049 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7053 g_critical("Houston, we've had a problem.");
7054 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7055 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7060 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7062 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7064 // Start trying to find same names again...
7065 waypoint_names = NULL;
7066 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7067 udata.has_same_waypoint_name = FALSE;
7068 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7070 // No waypoints any more - give up searching
7071 if ( ! dummy_list2 )
7072 udata.has_same_waypoint_name = FALSE;
7076 vik_layers_panel_emit_update ( vlp );
7082 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7084 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7087 // Ensure list of waypoint names offered is unique
7088 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7089 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7090 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7091 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7097 // Sort list alphabetically for better presentation
7098 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7100 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7104 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7106 // Get list of items to delete from the user
7107 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7110 _("Delete Selection"),
7111 _("Select waypoints to delete"));
7114 // Delete requested waypoints
7115 // since specificly requested, IMHO no need for extra confirmation
7116 if ( delete_list ) {
7118 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7119 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7120 trw_layer_delete_waypoint_by_name (vtl, l->data);
7122 g_list_free(delete_list);
7124 trw_layer_calculate_bounds_waypoints ( vtl );
7125 vik_layer_emit_update( VIK_LAYER(vtl) );
7133 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7135 vik_treeview_item_toggle_visible ( vt, it );
7141 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7143 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7149 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7151 wp->visible = GPOINTER_TO_INT (on_off);
7157 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7159 wp->visible = !wp->visible;
7165 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7167 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7168 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7169 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7170 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7172 vik_layer_emit_update ( VIK_LAYER(vtl) );
7178 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7180 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7181 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7182 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7183 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7185 vik_layer_emit_update ( VIK_LAYER(vtl) );
7191 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7193 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7194 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7195 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7197 vik_layer_emit_update ( VIK_LAYER(vtl) );
7203 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7205 trk->visible = GPOINTER_TO_INT (on_off);
7211 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7213 trk->visible = !trk->visible;
7219 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7221 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7222 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7223 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7224 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7226 vik_layer_emit_update ( VIK_LAYER(vtl) );
7232 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7234 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7235 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7236 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7237 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7239 vik_layer_emit_update ( VIK_LAYER(vtl) );
7245 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7247 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7248 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7249 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7251 vik_layer_emit_update ( VIK_LAYER(vtl) );
7257 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7259 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7260 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7261 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7262 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7264 vik_layer_emit_update ( VIK_LAYER(vtl) );
7270 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7272 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7273 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7274 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7275 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7277 vik_layer_emit_update ( VIK_LAYER(vtl) );
7283 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7285 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7286 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7287 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7289 vik_layer_emit_update ( VIK_LAYER(vtl) );
7293 * vik_trw_layer_build_waypoint_list_t:
7295 * Helper function to construct a list of #vik_trw_waypoint_list_t
7297 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7299 GList *waypoints_and_layers = NULL;
7300 // build waypoints_and_layers list
7301 while ( waypoints ) {
7302 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7303 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7305 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7306 waypoints = g_list_next ( waypoints );
7308 return waypoints_and_layers;
7312 * trw_layer_create_waypoint_list:
7314 * Create the latest list of waypoints with the associated layer(s)
7315 * Although this will always be from a single layer here
7317 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7319 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7320 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7322 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7326 * trw_layer_analyse_close:
7328 * Stuff to do on dialog closure
7330 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7332 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7333 gtk_widget_destroy ( dialog );
7334 vtl->tracks_analysis_dialog = NULL;
7338 * vik_trw_layer_build_track_list_t:
7340 * Helper function to construct a list of #vik_trw_track_list_t
7342 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7344 GList *tracks_and_layers = NULL;
7345 // build tracks_and_layers list
7347 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7348 vtdl->trk = VIK_TRACK(tracks->data);
7350 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7351 tracks = g_list_next ( tracks );
7353 return tracks_and_layers;
7357 * trw_layer_create_track_list:
7359 * Create the latest list of tracks with the associated layer(s)
7360 * Although this will always be from a single layer here
7362 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7364 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7365 GList *tracks = NULL;
7366 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7367 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7369 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7371 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7374 static void trw_layer_tracks_stats ( menu_array_layer values )
7376 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7377 // There can only be one!
7378 if ( vtl->tracks_analysis_dialog )
7381 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7382 VIK_LAYER(vtl)->name,
7384 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7385 trw_layer_create_track_list,
7386 trw_layer_analyse_close );
7392 static void trw_layer_routes_stats ( menu_array_layer values )
7394 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7395 // There can only be one!
7396 if ( vtl->tracks_analysis_dialog )
7399 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7400 VIK_LAYER(vtl)->name,
7402 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7403 trw_layer_create_track_list,
7404 trw_layer_analyse_close );
7407 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7409 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7410 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7412 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7415 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7417 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7418 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7421 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7422 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7426 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7428 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7429 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7433 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7434 } else if ( !strncmp(wp->comment, "http", 4) ) {
7435 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7436 } else if ( !strncmp(wp->description, "http", 4) ) {
7437 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7441 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7443 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7445 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7447 // No actual change to the name supplied
7449 if (strcmp(newname, wp->name) == 0 )
7452 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7455 // An existing waypoint has been found with the requested name
7456 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7457 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7462 // Update WP name and refresh the treeview
7463 vik_waypoint_set_name (wp, newname);
7465 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7466 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7468 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7473 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7475 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7477 // No actual change to the name supplied
7479 if (strcmp(newname, trk->name) == 0)
7482 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7485 // An existing track has been found with the requested name
7486 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7487 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7491 // Update track name and refresh GUI parts
7492 vik_track_set_name (trk, newname);
7494 // Update any subwindows that could be displaying this track which has changed name
7495 // Only one Track Edit Window
7496 if ( l->current_tp_track == trk && l->tpwin ) {
7497 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7499 // Property Dialog of the track
7500 vik_trw_layer_propwin_update ( trk );
7502 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7503 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7505 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7510 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7512 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7514 // No actual change to the name supplied
7516 if (strcmp(newname, trk->name) == 0)
7519 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7522 // An existing track has been found with the requested name
7523 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7524 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7528 // Update track name and refresh GUI parts
7529 vik_track_set_name (trk, newname);
7531 // Update any subwindows that could be displaying this track which has changed name
7532 // Only one Track Edit Window
7533 if ( l->current_tp_track == trk && l->tpwin ) {
7534 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7536 // Property Dialog of the track
7537 vik_trw_layer_propwin_update ( trk );
7539 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7540 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7542 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7549 static gboolean is_valid_geocache_name ( gchar *str )
7551 gint len = strlen ( str );
7552 return len >= 3 && len <= 7 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])) && (len < 7 || isalnum(str[6]));
7555 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7557 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7558 a_acquire_set_filter_track ( trk );
7561 #ifdef VIK_CONFIG_GOOGLE
7562 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7564 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7565 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7568 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7570 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7572 gchar *escaped = uri_escape ( tr->comment );
7573 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7574 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7581 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7582 /* viewpoint is now available instead */
7583 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7585 static menu_array_sublayer pass_along;
7587 gboolean rv = FALSE;
7589 pass_along[MA_VTL] = l;
7590 pass_along[MA_VLP] = vlp;
7591 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7592 pass_along[MA_SUBLAYER_ID] = sublayer;
7593 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7594 pass_along[MA_VVP] = vvp;
7595 pass_along[MA_TV_ITER] = iter;
7596 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7598 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7602 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7604 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7605 gtk_widget_show ( item );
7607 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7608 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7609 if (tr && tr->property_dialog)
7610 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7612 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7613 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7614 if (tr && tr->property_dialog)
7615 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7618 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7621 gtk_widget_show ( item );
7623 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7625 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7626 gtk_widget_show ( item );
7628 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7630 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7631 gtk_widget_show ( item );
7633 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7635 // Always create separator as now there is always at least the transform menu option
7636 item = gtk_menu_item_new ();
7637 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7638 gtk_widget_show ( item );
7640 /* could be a right-click using the tool */
7641 if ( vlp != NULL ) {
7642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7645 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7646 gtk_widget_show ( item );
7649 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7651 if ( wp && wp->name ) {
7652 if ( is_valid_geocache_name ( wp->name ) ) {
7653 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7656 gtk_widget_show ( item );
7658 #ifdef VIK_CONFIG_GEOTAG
7659 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7661 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7662 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7663 gtk_widget_show ( item );
7667 if ( wp && wp->image )
7669 // Set up image paramater
7670 pass_along[MA_MISC] = wp->image;
7672 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7673 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7674 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7675 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7676 gtk_widget_show ( item );
7678 #ifdef VIK_CONFIG_GEOTAG
7679 GtkWidget *geotag_submenu = gtk_menu_new ();
7680 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7681 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7682 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7683 gtk_widget_show ( item );
7684 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7686 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7687 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7688 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7689 gtk_widget_show ( item );
7691 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7693 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7694 gtk_widget_show ( item );
7701 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7702 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7703 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7704 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7705 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7706 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7707 gtk_widget_show ( item );
7713 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7714 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7717 gtk_widget_show ( item );
7718 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7719 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7720 gtk_widget_set_sensitive ( item, TRUE );
7722 gtk_widget_set_sensitive ( item, FALSE );
7725 item = gtk_menu_item_new ();
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7727 gtk_widget_show ( item );
7730 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7733 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7734 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7736 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7737 gtk_widget_show ( item );
7740 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7742 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7743 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7744 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7746 gtk_widget_show ( item );
7748 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7749 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7750 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7751 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7752 gtk_widget_show ( item );
7754 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7755 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7756 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7757 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7758 gtk_widget_show ( item );
7760 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7761 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7763 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7764 gtk_widget_show ( item );
7766 GtkWidget *vis_submenu = gtk_menu_new ();
7767 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7768 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7769 gtk_widget_show ( item );
7770 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7772 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7773 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7775 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7776 gtk_widget_show ( item );
7778 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7779 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7781 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7782 gtk_widget_show ( item );
7784 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7785 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7787 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7788 gtk_widget_show ( item );
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7793 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7796 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7800 if ( l->current_track && !l->current_track->is_route ) {
7801 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7803 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7804 gtk_widget_show ( item );
7806 item = gtk_menu_item_new ();
7807 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7808 gtk_widget_show ( item );
7811 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7812 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7814 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7815 gtk_widget_show ( item );
7817 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7818 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7819 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7820 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7821 gtk_widget_show ( item );
7822 // Make it available only when a new track *not* already in progress
7823 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7825 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7826 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7828 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7829 gtk_widget_show ( item );
7831 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7832 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7833 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7834 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7835 gtk_widget_show ( item );
7837 GtkWidget *vis_submenu = gtk_menu_new ();
7838 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7839 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7840 gtk_widget_show ( item );
7841 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7843 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7844 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7846 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7847 gtk_widget_show ( item );
7849 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7850 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7852 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7853 gtk_widget_show ( item );
7855 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7856 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7857 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7858 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7860 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7863 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7864 gtk_widget_show ( item );
7866 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7868 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7869 gtk_widget_show ( item );
7872 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7876 if ( l->current_track && l->current_track->is_route ) {
7877 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7878 // Reuse finish track method
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7880 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7881 gtk_widget_show ( item );
7883 item = gtk_menu_item_new ();
7884 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7885 gtk_widget_show ( item );
7888 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7889 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7890 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7891 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7892 gtk_widget_show ( item );
7894 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7895 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7897 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7898 gtk_widget_show ( item );
7899 // Make it available only when a new track *not* already in progress
7900 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7902 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7903 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7905 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7906 gtk_widget_show ( item );
7908 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7909 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7911 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7912 gtk_widget_show ( item );
7914 GtkWidget *vis_submenu = gtk_menu_new ();
7915 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7916 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7917 gtk_widget_show ( item );
7918 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7920 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7921 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7923 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7924 gtk_widget_show ( item );
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7929 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7930 gtk_widget_show ( item );
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7933 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7935 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7937 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7940 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7942 gtk_widget_show ( item );
7944 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7947 gtk_widget_show ( item );
7951 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7952 GtkWidget *submenu_sort = gtk_menu_new ();
7953 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7954 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7955 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7956 gtk_widget_show ( item );
7957 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7959 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7961 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7962 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7963 gtk_widget_show ( item );
7965 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7968 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7969 gtk_widget_show ( item );
7972 GtkWidget *upload_submenu = gtk_menu_new ();
7974 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7976 item = gtk_menu_item_new ();
7977 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7978 gtk_widget_show ( item );
7980 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7981 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7982 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7983 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7984 if ( l->current_track ) {
7985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7986 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7987 gtk_widget_show ( item );
7990 item = gtk_menu_item_new ();
7991 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7992 gtk_widget_show ( item );
7995 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7996 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7998 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7999 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8001 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8002 gtk_widget_show ( item );
8004 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8006 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8007 gtk_widget_show ( item );
8009 GtkWidget *goto_submenu;
8010 goto_submenu = gtk_menu_new ();
8011 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8012 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8013 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8014 gtk_widget_show ( item );
8015 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8017 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8018 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8020 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8021 gtk_widget_show ( item );
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8024 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8026 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8027 gtk_widget_show ( item );
8029 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8030 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8032 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8033 gtk_widget_show ( item );
8035 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8036 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8038 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8039 gtk_widget_show ( item );
8041 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8042 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8044 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8045 gtk_widget_show ( item );
8047 // Routes don't have speeds
8048 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8053 gtk_widget_show ( item );
8056 GtkWidget *combine_submenu;
8057 combine_submenu = gtk_menu_new ();
8058 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8059 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8060 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8061 gtk_widget_show ( item );
8062 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8064 // Routes don't have times or segments...
8065 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8066 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8068 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8069 gtk_widget_show ( item );
8071 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8073 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8074 gtk_widget_show ( item );
8077 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8079 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8080 gtk_widget_show ( item );
8082 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8083 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8085 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8087 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8088 gtk_widget_show ( item );
8090 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8091 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8093 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8095 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8096 gtk_widget_show ( item );
8098 GtkWidget *split_submenu;
8099 split_submenu = gtk_menu_new ();
8100 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8101 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8102 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8103 gtk_widget_show ( item );
8104 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8106 // Routes don't have times or segments...
8107 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8108 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8110 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8111 gtk_widget_show ( item );
8113 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8114 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8116 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8117 gtk_widget_show ( item );
8120 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8122 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8123 gtk_widget_show ( item );
8125 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8127 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8128 gtk_widget_show ( item );
8129 // Make it available only when a trackpoint is selected.
8130 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8132 GtkWidget *insert_submenu = gtk_menu_new ();
8133 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8135 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8136 gtk_widget_show ( item );
8137 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8139 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8141 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8142 gtk_widget_show ( item );
8143 // Make it available only when a point is selected
8144 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8146 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8148 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8149 gtk_widget_show ( item );
8150 // Make it available only when a point is selected
8151 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8153 GtkWidget *delete_submenu;
8154 delete_submenu = gtk_menu_new ();
8155 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8157 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8158 gtk_widget_show ( item );
8159 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8161 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8162 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8164 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8165 gtk_widget_show ( item );
8166 // Make it available only when a point is selected
8167 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8169 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8171 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8172 gtk_widget_show ( item );
8174 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8176 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8177 gtk_widget_show ( item );
8179 GtkWidget *transform_submenu;
8180 transform_submenu = gtk_menu_new ();
8181 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8182 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8183 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8184 gtk_widget_show ( item );
8185 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8187 GtkWidget *dem_submenu;
8188 dem_submenu = gtk_menu_new ();
8189 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8190 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8191 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8192 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8196 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8197 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8198 gtk_widget_show ( item );
8200 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8201 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8202 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8203 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8204 gtk_widget_show ( item );
8206 GtkWidget *smooth_submenu;
8207 smooth_submenu = gtk_menu_new ();
8208 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8209 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8210 gtk_widget_show ( item );
8211 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8213 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8215 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8216 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8217 gtk_widget_show ( item );
8219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8221 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8222 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8223 gtk_widget_show ( item );
8225 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8226 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8228 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8231 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8232 gtk_widget_show ( item );
8234 // Routes don't have timestamps - so this is only available for tracks
8235 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8236 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8237 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8238 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8239 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8240 gtk_widget_show ( item );
8243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8244 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8246 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8247 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8249 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8250 gtk_widget_show ( item );
8252 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8253 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8257 gtk_widget_show ( item );
8260 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8262 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8263 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8265 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8266 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8267 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8268 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8269 gtk_widget_show ( item );
8272 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8273 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8275 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8278 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8279 gtk_widget_show ( item );
8281 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8282 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8284 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8287 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8288 gtk_widget_show ( item );
8290 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8291 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8292 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8295 gtk_widget_show ( item );
8298 // ATM can't upload a single waypoint but can do waypoints to a GPS
8299 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8300 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8301 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8302 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8303 gtk_widget_show ( item );
8304 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8306 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8307 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8309 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8310 gtk_widget_show ( item );
8314 // Only made available if a suitable program is installed
8315 if ( have_diary_program ) {
8316 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8317 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8318 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8319 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8320 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8321 gtk_widget_show ( item );
8325 #ifdef VIK_CONFIG_GOOGLE
8326 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8328 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8329 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8330 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8331 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8332 gtk_widget_show ( item );
8336 // Some things aren't usable with routes
8337 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8338 #ifdef VIK_CONFIG_OPENSTREETMAP
8339 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8340 // Convert internal pointer into track
8341 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8342 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8344 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8345 gtk_widget_show ( item );
8348 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8349 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8350 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8351 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8352 gtk_widget_show ( item );
8354 /* ATM This function is only available via the layers panel, due to needing a vlp */
8356 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8357 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8358 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8360 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8361 gtk_widget_show ( item );
8365 #ifdef VIK_CONFIG_GEOTAG
8366 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8368 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8369 gtk_widget_show ( item );
8373 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8374 // Only show on viewport popmenu when a trackpoint is selected
8375 if ( ! vlp && l->current_tpl ) {
8377 item = gtk_menu_item_new ();
8378 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8379 gtk_widget_show ( item );
8381 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8382 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8384 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8385 gtk_widget_show ( item );
8389 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8390 GtkWidget *transform_submenu;
8391 transform_submenu = gtk_menu_new ();
8392 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8394 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8395 gtk_widget_show ( item );
8396 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8398 GtkWidget *dem_submenu;
8399 dem_submenu = gtk_menu_new ();
8400 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8401 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8402 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8403 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8407 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8408 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8409 gtk_widget_show ( item );
8411 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8412 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8413 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8414 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8415 gtk_widget_show ( item );
8418 gtk_widget_show_all ( GTK_WIDGET(menu) );
8423 // TODO: Probably better to rework this track manipulation in viktrack.c
8424 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8427 if (!vtl->current_tpl)
8430 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8431 VikTrackpoint *tp_other = NULL;
8434 if (!vtl->current_tpl->prev)
8436 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8438 if (!vtl->current_tpl->next)
8440 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8443 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8446 VikTrackpoint *tp_new = vik_trackpoint_new();
8447 struct LatLon ll_current, ll_other;
8448 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8449 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8451 /* main positional interpolation */
8452 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8453 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8455 /* Now other properties that can be interpolated */
8456 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8458 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8459 /* Note here the division is applied to each part, then added
8460 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8461 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8462 tp_new->has_timestamp = TRUE;
8465 if (tp_current->speed != NAN && tp_other->speed != NAN)
8466 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8468 /* TODO - improve interpolation of course, as it may not be correct.
8469 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8470 [similar applies if value is in radians] */
8471 if (tp_current->course != NAN && tp_other->course != NAN)
8472 tp_new->course = (tp_current->course + tp_other->course)/2;
8474 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8476 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8477 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8479 // Otherwise try routes
8480 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8484 gint index = g_list_index ( trk->trackpoints, tp_current );
8488 // NB no recalculation of bounds since it is inserted between points
8489 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8494 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8500 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8504 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8506 if ( vtl->current_tpl )
8508 vtl->current_tpl = NULL;
8509 vtl->current_tp_track = NULL;
8510 vtl->current_tp_id = NULL;
8511 vik_layer_emit_update(VIK_LAYER(vtl));
8515 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8517 VikTrack *trk = vtl->current_tp_track;
8519 // Notional center of a track is simply an average of the bounding box extremities
8520 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8521 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er );
8522 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8525 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8527 g_assert ( vtl->tpwin != NULL );
8528 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8529 trw_layer_cancel_current_tp ( vtl, TRUE );
8531 if ( vtl->current_tpl == NULL )
8534 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8536 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8537 my_tpwin_set_tp ( vtl );
8539 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8541 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8543 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8547 trw_layer_trackpoint_selected_delete ( vtl, tr );
8549 if ( vtl->current_tpl )
8550 // Reset dialog with the available adjacent trackpoint
8551 my_tpwin_set_tp ( vtl );
8553 vik_layer_emit_update(VIK_LAYER(vtl));
8555 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8557 if ( vtl->current_tp_track ) {
8558 vtl->current_tpl = vtl->current_tpl->next;
8559 my_tpwin_set_tp ( vtl );
8561 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8563 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8565 if ( vtl->current_tp_track ) {
8566 vtl->current_tpl = vtl->current_tpl->prev;
8567 my_tpwin_set_tp ( vtl );
8569 vik_layer_emit_update(VIK_LAYER(vtl));
8571 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8573 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8574 vik_layer_emit_update(VIK_LAYER(vtl));
8576 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8577 vik_layer_emit_update(VIK_LAYER(vtl));
8581 * trw_layer_dialog_shift:
8582 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8584 * Try to reposition a dialog if it's over the specified coord
8585 * so to not obscure the item of interest
8587 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8589 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8591 // Attempt force dialog to be shown so we can find out where it is more reliably...
8592 while ( gtk_events_pending() )
8593 gtk_main_iteration ();
8595 // get parent window position & size
8596 gint win_pos_x, win_pos_y;
8597 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8599 gint win_size_x, win_size_y;
8600 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8602 // get own dialog size
8603 gint dia_size_x, dia_size_y;
8604 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8606 // get own dialog position
8607 gint dia_pos_x, dia_pos_y;
8608 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8610 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8611 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8613 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8615 gint vp_xx, vp_yy; // In viewport pixels
8616 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8618 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8622 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8624 // Transform Viewport pixels into absolute pixels
8625 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8626 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8628 // Is dialog over the point (to within an ^^ edge value)
8629 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8630 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8634 gint hh = vik_viewport_get_height ( vvp );
8636 // Consider the difference in viewport to the full window
8637 gint offset_y = dest_y;
8638 // Add difference between dialog and window sizes
8639 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8641 if ( vp_yy > hh/2 ) {
8642 // Point in bottom half, move window to top half
8643 gtk_window_move ( dialog, dia_pos_x, offset_y );
8646 // Point in top half, move dialog down
8647 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8651 // Shift left<->right
8652 gint ww = vik_viewport_get_width ( vvp );
8654 // Consider the difference in viewport to the full window
8655 gint offset_x = dest_x;
8656 // Add difference between dialog and window sizes
8657 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8659 if ( vp_xx > ww/2 ) {
8660 // Point on right, move window to left
8661 gtk_window_move ( dialog, offset_x, dia_pos_y );
8664 // Point on left, move right
8665 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8673 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8677 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8678 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8679 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8680 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8682 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8684 if ( vtl->current_tpl ) {
8685 // get tp pixel position
8686 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8688 // Shift up<->down to try not to obscure the trackpoint.
8689 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8693 if ( vtl->current_tpl )
8694 if ( vtl->current_tp_track )
8695 my_tpwin_set_tp ( vtl );
8696 /* set layer name and TP data */
8699 /***************************************************************************
8701 ***************************************************************************/
8703 /*** Utility data structures and functions ****/
8707 gint closest_x, closest_y;
8708 gboolean draw_images;
8709 gpointer *closest_wp_id;
8710 VikWaypoint *closest_wp;
8716 gint closest_x, closest_y;
8717 gpointer closest_track_id;
8718 VikTrackpoint *closest_tp;
8724 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8730 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8732 // If waypoint has an image then use the image size to select
8733 if ( params->draw_images && wp->image ) {
8734 gint slackx, slacky;
8735 slackx = wp->image_width / 2;
8736 slacky = wp->image_height / 2;
8738 if ( x <= params->x + slackx && x >= params->x - slackx
8739 && y <= params->y + slacky && y >= params->y - slacky ) {
8740 params->closest_wp_id = id;
8741 params->closest_wp = wp;
8742 params->closest_x = x;
8743 params->closest_y = y;
8746 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8747 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8748 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8750 params->closest_wp_id = id;
8751 params->closest_wp = wp;
8752 params->closest_x = x;
8753 params->closest_y = y;
8757 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8759 GList *tpl = t->trackpoints;
8765 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8771 tp = VIK_TRACKPOINT(tpl->data);
8773 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8775 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8776 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8777 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8779 params->closest_track_id = id;
8780 params->closest_tp = tp;
8781 params->closest_tpl = tpl;
8782 params->closest_x = x;
8783 params->closest_y = y;
8789 // ATM: Leave this as 'Track' only.
8790 // Not overly bothered about having a snap to route trackpoint capability
8791 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8793 TPSearchParams params;
8797 params.closest_track_id = NULL;
8798 params.closest_tp = NULL;
8799 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8800 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8801 return params.closest_tp;
8804 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8806 WPSearchParams params;
8810 params.draw_images = vtl->drawimages;
8811 params.closest_wp = NULL;
8812 params.closest_wp_id = NULL;
8813 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8814 return params.closest_wp;
8818 // Some forward declarations
8819 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8820 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8821 static void marker_end_move ( tool_ed_t *t );
8824 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
8828 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8830 // Here always allow snapping back to the original location
8831 // this is useful when one decides not to move the thing afterall
8832 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8835 if ( event->state & GDK_CONTROL_MASK )
8837 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8839 new_coord = tp->coord;
8843 if ( event->state & GDK_SHIFT_MASK )
8845 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8847 new_coord = wp->coord;
8851 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8853 marker_moveto ( t, x, y );
8860 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8862 if ( t->holding && event->button == 1 )
8864 // Prevent accidental (small) shifts when specific movement has not been requested
8865 // (as the click release has occurred within the click object detection area)
8870 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8873 if ( event->state & GDK_CONTROL_MASK )
8875 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8877 new_coord = tp->coord;
8881 if ( event->state & GDK_SHIFT_MASK )
8883 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8885 new_coord = wp->coord;
8888 marker_end_move ( t );
8890 // Determine if working on a waypoint or a trackpoint
8891 if ( t->is_waypoint ) {
8892 // Update waypoint position
8893 vtl->current_wp->coord = new_coord;
8894 trw_layer_calculate_bounds_waypoints ( vtl );
8895 // Reset waypoint pointer
8896 vtl->current_wp = NULL;
8897 vtl->current_wp_id = NULL;
8900 if ( vtl->current_tpl ) {
8901 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8903 if ( vtl->current_tp_track )
8904 vik_track_calculate_bounds ( vtl->current_tp_track );
8907 if ( vtl->current_tp_track )
8908 my_tpwin_set_tp ( vtl );
8909 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8913 vik_layer_emit_update ( VIK_LAYER(vtl) );
8920 Returns true if a waypoint or track is found near the requested event position for this particular layer
8921 The item found is automatically selected
8922 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8924 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8926 if ( event->button != 1 )
8929 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8932 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8936 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8938 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8940 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8941 WPSearchParams wp_params;
8942 wp_params.vvp = vvp;
8943 wp_params.x = event->x;
8944 wp_params.y = event->y;
8945 wp_params.draw_images = vtl->drawimages;
8946 wp_params.closest_wp_id = NULL;
8947 wp_params.closest_wp = NULL;
8949 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8951 if ( wp_params.closest_wp ) {
8954 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8956 // Too easy to move it so must be holding shift to start immediately moving it
8957 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8958 if ( event->state & GDK_SHIFT_MASK ||
8959 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8960 // Put into 'move buffer'
8961 // NB vvp & vw already set in tet
8962 tet->vtl = (gpointer)vtl;
8963 tet->is_waypoint = TRUE;
8965 marker_begin_move (tet, event->x, event->y);
8968 vtl->current_wp = wp_params.closest_wp;
8969 vtl->current_wp_id = wp_params.closest_wp_id;
8971 if ( event->type == GDK_2BUTTON_PRESS ) {
8972 if ( vtl->current_wp->image ) {
8973 menu_array_sublayer values;
8974 values[MA_VTL] = vtl;
8975 values[MA_MISC] = vtl->current_wp->image;
8976 trw_layer_show_picture ( values );
8980 vik_layer_emit_update ( VIK_LAYER(vtl) );
8986 // Used for both track and route lists
8987 TPSearchParams tp_params;
8988 tp_params.vvp = vvp;
8989 tp_params.x = event->x;
8990 tp_params.y = event->y;
8991 tp_params.closest_track_id = NULL;
8992 tp_params.closest_tp = NULL;
8993 tp_params.closest_tpl = NULL;
8994 tp_params.bbox = bbox;
8996 if (vtl->tracks_visible) {
8997 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8999 if ( tp_params.closest_tp ) {
9001 // Always select + highlight the track
9002 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9004 tet->is_waypoint = FALSE;
9006 // Select the Trackpoint
9007 // Can move it immediately when control held or it's the previously selected tp
9008 if ( event->state & GDK_CONTROL_MASK ||
9009 vtl->current_tpl == tp_params.closest_tpl ) {
9010 // Put into 'move buffer'
9011 // NB vvp & vw already set in tet
9012 tet->vtl = (gpointer)vtl;
9013 marker_begin_move (tet, event->x, event->y);
9016 vtl->current_tpl = tp_params.closest_tpl;
9017 vtl->current_tp_id = tp_params.closest_track_id;
9018 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9020 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9023 my_tpwin_set_tp ( vtl );
9025 vik_layer_emit_update ( VIK_LAYER(vtl) );
9030 // Try again for routes
9031 if (vtl->routes_visible) {
9032 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9034 if ( tp_params.closest_tp ) {
9036 // Always select + highlight the track
9037 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9039 tet->is_waypoint = FALSE;
9041 // Select the Trackpoint
9042 // Can move it immediately when control held or it's the previously selected tp
9043 if ( event->state & GDK_CONTROL_MASK ||
9044 vtl->current_tpl == tp_params.closest_tpl ) {
9045 // Put into 'move buffer'
9046 // NB vvp & vw already set in tet
9047 tet->vtl = (gpointer)vtl;
9048 marker_begin_move (tet, event->x, event->y);
9051 vtl->current_tpl = tp_params.closest_tpl;
9052 vtl->current_tp_id = tp_params.closest_track_id;
9053 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9055 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9058 my_tpwin_set_tp ( vtl );
9060 vik_layer_emit_update ( VIK_LAYER(vtl) );
9065 /* these aren't the droids you're looking for */
9066 vtl->current_wp = NULL;
9067 vtl->current_wp_id = NULL;
9068 trw_layer_cancel_current_tp ( vtl, FALSE );
9071 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9076 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9078 if ( event->button != 3 )
9081 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9084 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9087 /* Post menu for the currently selected item */
9089 /* See if a track is selected */
9090 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9091 if ( track && track->visible ) {
9093 if ( track->name ) {
9095 if ( vtl->track_right_click_menu )
9096 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9098 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9105 if ( track->is_route )
9106 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9108 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9110 if ( trkf && udataU.uuid ) {
9113 if ( track->is_route )
9114 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9116 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9118 trw_layer_sublayer_add_menu_items ( vtl,
9119 vtl->track_right_click_menu,
9121 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9127 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9133 /* See if a waypoint is selected */
9134 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9135 if ( waypoint && waypoint->visible ) {
9136 if ( waypoint->name ) {
9138 if ( vtl->wp_right_click_menu )
9139 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9141 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9144 udata.wp = waypoint;
9147 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9149 if ( wpf && udata.uuid ) {
9150 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9152 trw_layer_sublayer_add_menu_items ( vtl,
9153 vtl->wp_right_click_menu,
9155 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9160 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9169 /* background drawing hook, to be passed the viewport */
9170 static gboolean tool_sync_done = TRUE;
9172 static gboolean tool_sync(gpointer data)
9174 VikViewport *vvp = data;
9175 gdk_threads_enter();
9176 vik_viewport_sync(vvp);
9177 tool_sync_done = TRUE;
9178 gdk_threads_leave();
9182 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9185 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9186 gdk_gc_set_function ( t->gc, GDK_INVERT );
9187 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9188 vik_viewport_sync(t->vvp);
9194 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9196 VikViewport *vvp = t->vvp;
9197 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9198 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9203 if (tool_sync_done) {
9204 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9205 tool_sync_done = FALSE;
9209 static void marker_end_move ( tool_ed_t *t )
9211 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9212 g_object_unref ( t->gc );
9217 /*** Edit waypoint ****/
9219 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9221 tool_ed_t *t = g_new(tool_ed_t, 1);
9227 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9232 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9234 WPSearchParams params;
9235 tool_ed_t *t = data;
9236 VikViewport *vvp = t->vvp;
9238 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9245 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9248 if ( vtl->current_wp && vtl->current_wp->visible )
9250 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9252 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9254 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9255 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9257 if ( event->button == 3 )
9258 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9260 marker_begin_move(t, event->x, event->y);
9267 params.x = event->x;
9268 params.y = event->y;
9269 params.draw_images = vtl->drawimages;
9270 params.closest_wp_id = NULL;
9271 params.closest_wp = NULL;
9272 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9273 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9275 if ( event->button == 3 )
9276 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9278 marker_begin_move(t, event->x, event->y);
9281 else if ( params.closest_wp )
9283 if ( event->button == 3 )
9284 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9286 vtl->waypoint_rightclick = FALSE;
9288 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9290 vtl->current_wp = params.closest_wp;
9291 vtl->current_wp_id = params.closest_wp_id;
9293 /* could make it so don't update if old WP is off screen and new is null but oh well */
9294 vik_layer_emit_update ( VIK_LAYER(vtl) );
9298 vtl->current_wp = NULL;
9299 vtl->current_wp_id = NULL;
9300 vtl->waypoint_rightclick = FALSE;
9301 vik_layer_emit_update ( VIK_LAYER(vtl) );
9305 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9307 tool_ed_t *t = data;
9308 VikViewport *vvp = t->vvp;
9310 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9315 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9318 if ( event->state & GDK_CONTROL_MASK )
9320 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9322 new_coord = tp->coord;
9326 if ( event->state & GDK_SHIFT_MASK )
9328 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9329 if ( wp && wp != vtl->current_wp )
9330 new_coord = wp->coord;
9335 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9337 marker_moveto ( t, x, y );
9344 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9346 tool_ed_t *t = data;
9347 VikViewport *vvp = t->vvp;
9349 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9352 if ( t->holding && event->button == 1 )
9355 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9358 if ( event->state & GDK_CONTROL_MASK )
9360 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9362 new_coord = tp->coord;
9366 if ( event->state & GDK_SHIFT_MASK )
9368 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9369 if ( wp && wp != vtl->current_wp )
9370 new_coord = wp->coord;
9373 marker_end_move ( t );
9375 vtl->current_wp->coord = new_coord;
9377 trw_layer_calculate_bounds_waypoints ( vtl );
9378 vik_layer_emit_update ( VIK_LAYER(vtl) );
9381 /* PUT IN RIGHT PLACE!!! */
9382 if ( event->button == 3 && vtl->waypoint_rightclick )
9384 if ( vtl->wp_right_click_menu )
9385 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9386 if ( vtl->current_wp ) {
9387 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9388 trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
9389 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9391 vtl->waypoint_rightclick = FALSE;
9396 /*** New track ****/
9398 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9405 GdkDrawable *drawable;
9411 * Draw specified pixmap
9413 static gboolean draw_sync ( gpointer data )
9415 draw_sync_t *ds = (draw_sync_t*) data;
9416 // Sometimes don't want to draw
9417 // normally because another update has taken precedent such as panning the display
9418 // which means this pixmap is no longer valid
9419 if ( ds->vtl->draw_sync_do ) {
9420 gdk_threads_enter();
9421 gdk_draw_drawable (ds->drawable,
9424 0, 0, 0, 0, -1, -1);
9425 ds->vtl->draw_sync_done = TRUE;
9426 gdk_threads_leave();
9432 static gchar* distance_string (gdouble distance)
9436 /* draw label with distance */
9437 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9438 switch (dist_units) {
9439 case VIK_UNITS_DISTANCE_MILES:
9440 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9441 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9442 } else if (distance < 1609.4) {
9443 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9445 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9448 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9449 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9450 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9451 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9452 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9454 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9458 // VIK_UNITS_DISTANCE_KILOMETRES
9459 if (distance >= 1000 && distance < 100000) {
9460 g_sprintf(str, "%3.2f km", distance/1000.0);
9461 } else if (distance < 1000) {
9462 g_sprintf(str, "%d m", (int)distance);
9464 g_sprintf(str, "%d km", (int)distance/1000);
9468 return g_strdup (str);
9472 * Actually set the message in statusbar
9474 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9476 // Only show elevation data when track has some elevation properties
9477 gchar str_gain_loss[64];
9478 str_gain_loss[0] = '\0';
9479 gchar str_last_step[64];
9480 str_last_step[0] = '\0';
9481 gchar *str_total = distance_string (distance);
9483 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9484 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9485 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9487 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9490 if ( last_step > 0 ) {
9491 gchar *tmp = distance_string (last_step);
9492 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9496 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9498 // Write with full gain/loss information
9499 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9500 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9502 g_free ( str_total );
9506 * Figure out what information should be set in the statusbar and then write it
9508 static void update_statusbar ( VikTrwLayer *vtl )
9510 // Get elevation data
9511 gdouble elev_gain, elev_loss;
9512 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9514 /* Find out actual distance of current track */
9515 gdouble distance = vik_track_get_length (vtl->current_track);
9517 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9521 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9523 /* if we haven't sync'ed yet, we don't have time to do more. */
9524 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9525 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9527 static GdkPixmap *pixmap = NULL;
9529 // Need to check in case window has been resized
9530 w1 = vik_viewport_get_width(vvp);
9531 h1 = vik_viewport_get_height(vvp);
9533 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9535 gdk_drawable_get_size (pixmap, &w2, &h2);
9536 if (w1 != w2 || h1 != h2) {
9537 g_object_unref ( G_OBJECT ( pixmap ) );
9538 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9541 // Reset to background
9542 gdk_draw_drawable (pixmap,
9543 vtl->current_track_newpoint_gc,
9544 vik_viewport_get_pixmap(vvp),
9545 0, 0, 0, 0, -1, -1);
9547 draw_sync_t *passalong;
9550 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9552 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9553 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9554 // thus when we come to reset to the background it would include what we have already drawn!!
9555 gdk_draw_line ( pixmap,
9556 vtl->current_track_newpoint_gc,
9557 x1, y1, event->x, event->y );
9558 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9560 /* Find out actual distance of current track */
9561 gdouble distance = vik_track_get_length (vtl->current_track);
9563 // Now add distance to where the pointer is //
9566 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9567 vik_coord_to_latlon ( &coord, &ll );
9568 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9569 distance = distance + last_step;
9571 // Get elevation data
9572 gdouble elev_gain, elev_loss;
9573 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9575 // Adjust elevation data (if available) for the current pointer position
9577 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9578 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9579 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9580 // Adjust elevation of last track point
9581 if ( elev_new > last_tpt->altitude )
9583 elev_gain += elev_new - last_tpt->altitude;
9586 elev_loss += last_tpt->altitude - elev_new;
9591 // Display of the distance 'tooltip' during track creation is controlled by a preference
9593 if ( a_vik_get_create_track_tooltip() ) {
9595 gchar *str = distance_string (distance);
9597 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9598 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9599 pango_layout_set_text (pl, str, -1);
9601 pango_layout_get_pixel_size ( pl, &wd, &hd );
9604 // offset from cursor a bit depending on font size
9608 // Create a background block to make the text easier to read over the background map
9609 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9610 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9611 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9613 g_object_unref ( G_OBJECT ( pl ) );
9614 g_object_unref ( G_OBJECT ( background_block_gc ) );
9618 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9619 passalong->vtl = vtl;
9620 passalong->pixmap = pixmap;
9621 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9622 passalong->gc = vtl->current_track_newpoint_gc;
9626 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9628 // Update statusbar with full gain/loss information
9629 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9631 // draw pixmap when we have time to
9632 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9633 vtl->draw_sync_done = FALSE;
9634 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9636 return VIK_LAYER_TOOL_ACK;
9639 // NB vtl->current_track must be valid
9640 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9643 if ( vtl->current_track->trackpoints ) {
9644 // TODO rework this...
9645 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9646 GList *last = g_list_last(vtl->current_track->trackpoints);
9647 g_free ( last->data );
9648 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9650 vik_track_calculate_bounds ( vtl->current_track );
9654 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9656 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9657 // Bin track if only one point as it's not very useful
9658 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9659 if ( vtl->current_track->is_route )
9660 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9662 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9664 vtl->current_track = NULL;
9665 vik_layer_emit_update ( VIK_LAYER(vtl) );
9667 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9668 undo_trackpoint_add ( vtl );
9669 update_statusbar ( vtl );
9670 vik_layer_emit_update ( VIK_LAYER(vtl) );
9677 * Common function to handle trackpoint button requests on either a route or a track
9678 * . enables adding a point via normal click
9679 * . enables removal of last point via right click
9680 * . finishing of the track or route via double clicking
9682 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9686 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9689 if ( event->button == 2 ) {
9690 // As the display is panning, the new track pixmap is now invalid so don't draw it
9691 // otherwise this drawing done results in flickering back to an old image
9692 vtl->draw_sync_do = FALSE;
9696 if ( event->button == 3 )
9698 if ( !vtl->current_track )
9700 undo_trackpoint_add ( vtl );
9701 update_statusbar ( vtl );
9702 vik_layer_emit_update ( VIK_LAYER(vtl) );
9706 if ( event->type == GDK_2BUTTON_PRESS )
9708 /* subtract last (duplicate from double click) tp then end */
9709 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9711 /* undo last, then end */
9712 undo_trackpoint_add ( vtl );
9713 vtl->current_track = NULL;
9715 vik_layer_emit_update ( VIK_LAYER(vtl) );
9719 tp = vik_trackpoint_new();
9720 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9722 /* snap to other TP */
9723 if ( event->state & GDK_CONTROL_MASK )
9725 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9727 tp->coord = other_tp->coord;
9730 tp->newsegment = FALSE;
9731 tp->has_timestamp = FALSE;
9734 if ( vtl->current_track ) {
9735 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9736 /* Auto attempt to get elevation from DEM data (if it's available) */
9737 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9740 vtl->ct_x1 = vtl->ct_x2;
9741 vtl->ct_y1 = vtl->ct_y2;
9742 vtl->ct_x2 = event->x;
9743 vtl->ct_y2 = event->y;
9745 vik_layer_emit_update ( VIK_LAYER(vtl) );
9749 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9751 // if we were running the route finder, cancel it
9752 vtl->route_finder_started = FALSE;
9754 // ----------------------------------------------------- if current is a route - switch to new track
9755 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9757 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9758 if ( a_vik_get_ask_for_create_track_name() ) {
9759 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
9763 new_track_create_common ( vtl, name );
9766 return tool_new_track_or_route_click ( vtl, event, vvp );
9769 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9771 if ( event->button == 2 ) {
9772 // Pan moving ended - enable potential point drawing again
9773 vtl->draw_sync_do = TRUE;
9774 vtl->draw_sync_done = TRUE;
9778 /*** New route ****/
9780 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9785 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9787 // if we were running the route finder, cancel it
9788 vtl->route_finder_started = FALSE;
9790 // -------------------------- if current is a track - switch to new route,
9791 if ( event->button == 1 && ( ! vtl->current_track ||
9792 (vtl->current_track && !vtl->current_track->is_route ) ) )
9794 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9795 if ( a_vik_get_ask_for_create_track_name() ) {
9796 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
9800 new_route_create_common ( vtl, name );
9803 return tool_new_track_or_route_click ( vtl, event, vvp );
9806 /*** New waypoint ****/
9808 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9813 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9816 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9818 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9819 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9820 trw_layer_calculate_bounds_waypoints ( vtl );
9821 vik_layer_emit_update ( VIK_LAYER(vtl) );
9827 /*** Edit trackpoint ****/
9829 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9831 tool_ed_t *t = g_new(tool_ed_t, 1);
9837 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9842 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9844 tool_ed_t *t = data;
9845 VikViewport *vvp = t->vvp;
9846 TPSearchParams params;
9847 /* OUTDATED DOCUMENTATION:
9848 find 5 pixel range on each side. then put these UTM, and a pointer
9849 to the winning track name (and maybe the winning track itself), and a
9850 pointer to the winning trackpoint, inside an array or struct. pass
9851 this along, do a foreach on the tracks which will do a foreach on the
9854 params.x = event->x;
9855 params.y = event->y;
9856 params.closest_track_id = NULL;
9857 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9858 params.closest_tp = NULL;
9859 params.closest_tpl = NULL;
9860 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9862 if ( event->button != 1 )
9865 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9868 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9871 if ( vtl->current_tpl )
9873 /* first check if it is within range of prev. tp. and if current_tp track is shown. (if it is, we are moving that trackpoint.) */
9874 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9875 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9880 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9882 if ( current_tr->visible &&
9883 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9884 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9885 marker_begin_move ( t, event->x, event->y );
9891 if ( vtl->tracks_visible )
9892 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9894 if ( params.closest_tp )
9896 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9897 vtl->current_tpl = params.closest_tpl;
9898 vtl->current_tp_id = params.closest_track_id;
9899 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9900 trw_layer_tpwin_init ( vtl );
9901 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9902 vik_layer_emit_update ( VIK_LAYER(vtl) );
9906 if ( vtl->routes_visible )
9907 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9909 if ( params.closest_tp )
9911 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9912 vtl->current_tpl = params.closest_tpl;
9913 vtl->current_tp_id = params.closest_track_id;
9914 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9915 trw_layer_tpwin_init ( vtl );
9916 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9917 vik_layer_emit_update ( VIK_LAYER(vtl) );
9921 /* these aren't the droids you're looking for */
9925 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9927 tool_ed_t *t = data;
9928 VikViewport *vvp = t->vvp;
9930 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9936 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9939 if ( event->state & GDK_CONTROL_MASK )
9941 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9942 if ( tp && tp != vtl->current_tpl->data )
9943 new_coord = tp->coord;
9945 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9948 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9949 marker_moveto ( t, x, y );
9957 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9959 tool_ed_t *t = data;
9960 VikViewport *vvp = t->vvp;
9962 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9964 if ( event->button != 1)
9969 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9972 if ( event->state & GDK_CONTROL_MASK )
9974 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9975 if ( tp && tp != vtl->current_tpl->data )
9976 new_coord = tp->coord;
9979 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9980 if ( vtl->current_tp_track )
9981 vik_track_calculate_bounds ( vtl->current_tp_track );
9983 marker_end_move ( t );
9985 /* diff dist is diff from orig */
9987 my_tpwin_set_tp ( vtl );
9989 vik_layer_emit_update ( VIK_LAYER(vtl) );
9996 /*** Extended Route Finder ***/
9998 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10003 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10006 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10008 g_free ( new_end );
10009 vik_layer_emit_update ( VIK_LAYER(vtl) );
10011 /* remove last ' to:...' */
10012 if ( vtl->current_track->comment ) {
10013 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10014 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10015 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10016 last_to - vtl->current_track->comment - 1);
10017 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10024 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10027 if ( !vtl ) return FALSE;
10028 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10029 if ( event->button == 3 && vtl->current_track ) {
10030 tool_extended_route_finder_undo ( vtl );
10032 else if ( event->button == 2 ) {
10033 vtl->draw_sync_do = FALSE;
10036 // if we started the track but via undo deleted all the track points, begin again
10037 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10038 return tool_new_track_or_route_click ( vtl, event, vvp );
10040 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10041 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10042 struct LatLon start, end;
10044 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10045 vik_coord_to_latlon ( &(tp_start->coord), &start );
10046 vik_coord_to_latlon ( &(tmp), &end );
10048 vtl->route_finder_started = TRUE;
10049 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10051 // update UI to let user know what's going on
10052 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10053 VikRoutingEngine *engine = vik_routing_default_engine ( );
10055 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10058 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10059 vik_routing_engine_get_label ( engine ),
10060 start.lat, start.lon, end.lat, end.lon );
10061 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10063 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10066 /* Give GTK a change to display the new status bar before querying the web */
10067 while ( gtk_events_pending ( ) )
10068 gtk_main_iteration ( );
10070 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10072 /* Update UI to say we're done */
10073 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10074 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10075 vik_routing_engine_get_label ( engine ),
10076 start.lat, start.lon, end.lat, end.lon )
10077 : g_strdup_printf ( _("Error getting route from %s."),
10078 vik_routing_engine_get_label ( engine ) );
10079 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10082 vik_layer_emit_update ( VIK_LAYER(vtl) );
10084 vtl->current_track = NULL;
10086 // create a new route where we will add the planned route to
10087 gboolean ret = tool_new_route_click( vtl, event, vvp );
10089 vtl->route_finder_started = TRUE;
10096 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10098 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10099 vtl->route_finder_started = FALSE;
10100 vtl->current_track = NULL;
10101 vik_layer_emit_update ( VIK_LAYER(vtl) );
10103 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10104 tool_extended_route_finder_undo ( vtl );
10111 /*** Show picture ****/
10113 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10118 /* Params are: vvp, event, last match found or NULL */
10119 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10121 if ( wp->image && wp->visible )
10123 gint x, y, slackx, slacky;
10124 GdkEventButton *event = (GdkEventButton *) params[1];
10126 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10127 slackx = wp->image_width / 2;
10128 slacky = wp->image_height / 2;
10129 if ( x <= event->x + slackx && x >= event->x - slackx
10130 && y <= event->y + slacky && y >= event->y - slacky )
10132 params[2] = wp->image; /* we've found a match. however continue searching
10133 * since we want to find the last match -- that
10134 * is, the match that was drawn last. */
10139 static void trw_layer_show_picture ( menu_array_sublayer values )
10141 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10143 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10144 #else /* WINDOWS */
10145 GError *err = NULL;
10146 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10147 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10148 g_free ( quoted_file );
10149 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10151 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
10152 g_error_free ( err );
10155 #endif /* WINDOWS */
10158 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10160 gpointer params[3] = { vvp, event, NULL };
10161 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10163 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10166 static menu_array_sublayer values;
10167 values[MA_VTL] = vtl;
10168 values[MA_MISC] = params[2];
10169 trw_layer_show_picture ( values );
10170 return TRUE; /* found a match */
10173 return FALSE; /* go through other layers, searching for a match */
10176 /***************************************************************************
10178 ***************************************************************************/
10181 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10183 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10184 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10187 /* Structure for thumbnail creating data used in the background thread */
10189 VikTrwLayer *vtl; // Layer needed for redrawing
10190 GSList *pics; // Image list
10191 } thumbnail_create_thread_data;
10193 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10195 guint total = g_slist_length(tctd->pics), done = 0;
10196 while ( tctd->pics )
10198 a_thumbnails_create ( (gchar *) tctd->pics->data );
10199 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10201 return -1; /* Abort thread */
10203 tctd->pics = tctd->pics->next;
10206 // Redraw to show the thumbnails as they are now created
10207 if ( IS_VIK_LAYER(tctd->vtl) )
10208 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10213 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10215 while ( tctd->pics )
10217 g_free ( tctd->pics->data );
10218 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10223 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10225 if ( ! vtl->has_verified_thumbnails )
10227 GSList *pics = NULL;
10228 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10231 gint len = g_slist_length ( pics );
10232 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10233 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10236 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10238 (vik_thr_func) create_thumbnails_thread,
10240 (vik_thr_free_func) thumbnail_create_thread_free,
10248 static const gchar* my_track_colors ( gint ii )
10250 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10262 // Fast and reliable way of returning a colour
10263 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10266 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10268 GHashTableIter iter;
10269 gpointer key, value;
10273 g_hash_table_iter_init ( &iter, vtl->tracks );
10275 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10277 // Tracks get a random spread of colours if not already assigned
10278 if ( ! VIK_TRACK(value)->has_color ) {
10279 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10280 VIK_TRACK(value)->color = vtl->track_color;
10282 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10284 VIK_TRACK(value)->has_color = TRUE;
10287 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10290 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10296 g_hash_table_iter_init ( &iter, vtl->routes );
10298 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10300 // Routes get an intermix of reds
10301 if ( ! VIK_TRACK(value)->has_color ) {
10303 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10305 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10306 VIK_TRACK(value)->has_color = TRUE;
10309 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10316 * (Re)Calculate the bounds of the waypoints in this layer,
10317 * This should be called whenever waypoints are changed
10319 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10321 struct LatLon topleft = { 0.0, 0.0 };
10322 struct LatLon bottomright = { 0.0, 0.0 };
10325 GHashTableIter iter;
10326 gpointer key, value;
10328 g_hash_table_iter_init ( &iter, vtl->waypoints );
10330 // Set bounds to first point
10331 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10332 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10333 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10336 // Ensure there is another point...
10337 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10339 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10341 // See if this point increases the bounds.
10342 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10344 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10345 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10346 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10347 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10351 vtl->waypoints_bbox.north = topleft.lat;
10352 vtl->waypoints_bbox.east = bottomright.lon;
10353 vtl->waypoints_bbox.south = bottomright.lat;
10354 vtl->waypoints_bbox.west = topleft.lon;
10357 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10359 vik_track_calculate_bounds ( trk );
10362 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10364 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10365 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10368 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10370 if ( ! VIK_LAYER(vtl)->vt )
10373 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10374 if ( g_hash_table_size (vtl->tracks) > 1 )
10375 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10377 if ( g_hash_table_size (vtl->routes) > 1 )
10378 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10380 if ( g_hash_table_size (vtl->waypoints) > 1 )
10381 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10384 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10386 if ( VIK_LAYER(vtl)->realized )
10387 trw_layer_verify_thumbnails ( vtl, vvp );
10388 trw_layer_track_alloc_colors ( vtl );
10390 trw_layer_calculate_bounds_waypoints ( vtl );
10391 trw_layer_calculate_bounds_tracks ( vtl );
10393 // Apply treeview sort after loading all the tracks for this layer
10394 // (rather than sorted insert on each individual track additional)
10395 // and after subsequent changes to the properties as the specified order may have changed.
10396 // since the sorting of a treeview section is now very quick
10397 // NB sorting is also performed after every name change as well to maintain the list order
10398 trw_layer_sort_all ( vtl );
10400 // Setting metadata time if not otherwise set
10401 if ( vtl->metadata ) {
10403 gboolean need_to_set_time = TRUE;
10404 if ( vtl->metadata->timestamp ) {
10405 need_to_set_time = FALSE;
10406 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10407 need_to_set_time = TRUE;
10410 if ( need_to_set_time ) {
10411 // Could rewrite this as a general get first time of a TRW Layer function
10412 GTimeVal timestamp;
10413 timestamp.tv_usec = 0;
10414 gboolean has_timestamp = FALSE;
10417 gl = g_hash_table_get_values ( vtl->tracks );
10418 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10419 gl = g_list_first ( gl );
10421 // Check times of tracks
10423 // Only need to check the first track as they have been sorted by time
10424 VikTrack *trk = (VikTrack*)gl->data;
10425 // Assume trackpoints already sorted by time
10426 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10427 if ( tpt && tpt->has_timestamp ) {
10428 timestamp.tv_sec = tpt->timestamp;
10429 has_timestamp = TRUE;
10431 g_list_free ( gl );
10434 if ( !has_timestamp ) {
10435 // 'Last' resort - current time
10436 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10437 g_get_current_time ( ×tamp );
10439 // Check times of waypoints
10440 gl = g_hash_table_get_values ( vtl->waypoints );
10442 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10443 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10444 if ( wpt->has_timestamp ) {
10445 if ( timestamp.tv_sec > wpt->timestamp ) {
10446 timestamp.tv_sec = wpt->timestamp;
10447 has_timestamp = TRUE;
10451 g_list_free ( gl );
10454 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10459 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10461 return vtl->coord_mode;
10465 * Uniquify the whole layer
10466 * Also requires the layers panel as the names shown there need updating too
10467 * Returns whether the operation was successful or not
10469 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10471 if ( vtl && vlp ) {
10472 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10473 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10474 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10480 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10482 vik_coord_convert ( &(wp->coord), *dest_mode );
10485 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10487 vik_track_convert ( tr, *dest_mode );
10490 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10492 if ( vtl->coord_mode != dest_mode )
10494 vtl->coord_mode = dest_mode;
10495 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10496 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10497 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10501 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10503 vtl->menu_selection = selection;
10506 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10508 return (vtl->menu_selection);
10511 /* ----------- Downloading maps along tracks --------------- */
10513 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10515 /* TODO: calculating based on current size of viewport */
10516 const gdouble w_at_zoom_0_125 = 0.0013;
10517 const gdouble h_at_zoom_0_125 = 0.0011;
10518 gdouble zoom_factor = zoom_level/0.125;
10520 wh->lat = h_at_zoom_0_125 * zoom_factor;
10521 wh->lon = w_at_zoom_0_125 * zoom_factor;
10523 return 0; /* all OK */
10526 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10528 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10529 (dist->lat >= ABS(to->north_south - from->north_south)))
10532 VikCoord *coord = g_malloc(sizeof(VikCoord));
10533 coord->mode = VIK_COORD_LATLON;
10535 if (ABS(gradient) < 1) {
10536 if (from->east_west > to->east_west)
10537 coord->east_west = from->east_west - dist->lon;
10539 coord->east_west = from->east_west + dist->lon;
10540 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10542 if (from->north_south > to->north_south)
10543 coord->north_south = from->north_south - dist->lat;
10545 coord->north_south = from->north_south + dist->lat;
10546 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10552 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10554 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10555 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10557 VikCoord *next = from;
10559 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10561 list = g_list_prepend(list, next);
10567 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10569 typedef struct _Rect {
10574 #define GLRECT(iter) ((Rect *)((iter)->data))
10577 GList *rects_to_download = NULL;
10580 if (get_download_area_width(vvp, zoom_level, &wh))
10583 GList *iter = tr->trackpoints;
10587 gboolean new_map = TRUE;
10588 VikCoord *cur_coord, tl, br;
10591 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10593 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10594 rect = g_malloc(sizeof(Rect));
10597 rect->center = *cur_coord;
10598 rects_to_download = g_list_prepend(rects_to_download, rect);
10603 gboolean found = FALSE;
10604 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10605 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10616 GList *fillins = NULL;
10617 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10618 /* seems that ATM the function get_next_coord works only for LATLON */
10619 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10620 /* fill-ins for far apart points */
10621 GList *cur_rect, *next_rect;
10622 for (cur_rect = rects_to_download;
10623 (next_rect = cur_rect->next) != NULL;
10624 cur_rect = cur_rect->next) {
10625 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10626 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10627 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10631 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10634 GList *fiter = fillins;
10636 cur_coord = (VikCoord *)(fiter->data);
10637 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10638 rect = g_malloc(sizeof(Rect));
10641 rect->center = *cur_coord;
10642 rects_to_download = g_list_prepend(rects_to_download, rect);
10643 fiter = fiter->next;
10647 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10648 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10652 for (iter = fillins; iter; iter = iter->next)
10653 g_free(iter->data);
10654 g_list_free(fillins);
10656 if (rects_to_download) {
10657 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10658 g_free(rect_iter->data);
10659 g_list_free(rects_to_download);
10663 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10667 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10668 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10669 gint selected_zoom, default_zoom;
10671 VikTrwLayer *vtl = values[MA_VTL];
10672 VikLayersPanel *vlp = values[MA_VLP];
10674 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10675 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10677 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10681 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10683 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10684 int num_maps = g_list_length(vmls);
10687 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10691 // Convert from list of vmls to list of names. Allowing the user to select one of them
10692 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10693 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10695 gchar **np = map_names;
10696 VikMapsLayer **lp = map_layers;
10698 for (i = 0; i < num_maps; i++) {
10699 vml = (VikMapsLayer *)(vmls->data);
10701 *np++ = vik_maps_layer_get_map_label(vml);
10704 // Mark end of the array lists
10708 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10709 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10710 if (cur_zoom == zoom_vals[default_zoom])
10713 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10715 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10718 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10721 for (i = 0; i < num_maps; i++)
10722 g_free(map_names[i]);
10724 g_free(map_layers);
10730 /**** lowest waypoint number calculation ***/
10731 static gint highest_wp_number_name_to_number(const gchar *name) {
10732 if ( strlen(name) == 3 ) {
10733 int n = atoi(name);
10734 if ( n < 100 && name[0] != '0' )
10736 if ( n < 10 && name[0] != '0' )
10744 static void highest_wp_number_reset(VikTrwLayer *vtl)
10746 vtl->highest_wp_number = -1;
10749 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10751 /* if is bigger that top, add it */
10752 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10753 if ( new_wp_num > vtl->highest_wp_number )
10754 vtl->highest_wp_number = new_wp_num;
10757 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10759 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10760 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10761 if ( vtl->highest_wp_number == old_wp_num ) {
10763 vtl->highest_wp_number--;
10765 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10766 /* search down until we find something that *does* exist */
10768 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10769 vtl->highest_wp_number--;
10770 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10775 /* get lowest unused number */
10776 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10779 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10781 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10782 return g_strdup(buf);
10786 * trw_layer_create_track_list_both:
10788 * Create the latest list of tracks and routes
10790 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10792 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10793 GList *tracks = NULL;
10794 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10795 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10797 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10800 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10802 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10804 gchar *title = NULL;
10805 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10806 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10808 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10810 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10814 static void trw_layer_track_list_dialog ( menu_array_layer values )
10816 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10818 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10819 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10823 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10825 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10827 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10828 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );