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 ( mystdout && 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 VikTrackpoint *trkpt_prev = NULL;
3003 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3004 // Otherwise use default
3005 statusbar_format_code = g_strdup ( "KEATDN" );
3009 // Format code may want to show speed - so may need previous trkpt to work it out
3010 trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
3013 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
3014 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3018 g_free ( statusbar_format_code );
3022 * Function to show basic waypoint information on the statusbar
3024 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3027 switch (a_vik_get_units_height ()) {
3028 case VIK_UNITS_HEIGHT_FEET:
3029 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3032 //VIK_UNITS_HEIGHT_METRES:
3033 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3037 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3038 // one can easily use the current pointer position to see this if needed
3039 gchar *lat = NULL, *lon = NULL;
3040 static struct LatLon ll;
3041 vik_coord_to_latlon (&(wpt->coord), &ll);
3042 a_coords_latlon_to_string ( &ll, &lat, &lon );
3044 // Combine parts to make overall message
3047 // Add comment if available
3048 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3050 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3051 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3058 * General layer selection function, find out which bit is selected and take appropriate action
3060 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3063 l->current_wp = NULL;
3064 l->current_wp_id = NULL;
3065 trw_layer_cancel_current_tp ( l, FALSE );
3068 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3072 case VIK_TREEVIEW_TYPE_LAYER:
3074 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3075 /* Mark for redraw */
3080 case VIK_TREEVIEW_TYPE_SUBLAYER:
3084 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3086 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3087 /* Mark for redraw */
3091 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3093 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3094 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3095 /* Mark for redraw */
3099 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3101 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3102 /* Mark for redraw */
3106 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3108 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3109 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3110 /* Mark for redraw */
3114 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3116 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3117 /* Mark for redraw */
3121 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3123 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3125 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3126 // Show some waypoint info
3127 set_statusbar_msg_info_wpt ( l, wpt );
3128 /* Mark for redraw */
3135 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3144 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3149 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3154 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3159 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3161 return l->waypoints;
3164 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3166 return vtl->tracks_iters;
3169 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3171 return vtl->routes_iters;
3174 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3176 return vtl->waypoints;
3179 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3181 return ! ( g_hash_table_size ( vtl->tracks ) ||
3182 g_hash_table_size ( vtl->routes ) ||
3183 g_hash_table_size ( vtl->waypoints ) );
3186 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3188 return vtl->tracks_visible;
3191 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3193 return vtl->routes_visible;
3196 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3198 return vtl->waypoints_visible;
3202 * ATM use a case sensitive find
3203 * Finds the first one
3205 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3207 if ( wp && wp->name )
3208 if ( ! strcmp ( wp->name, name ) )
3214 * Get waypoint by name - not guaranteed to be unique
3215 * Finds the first one
3217 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3219 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3223 * ATM use a case sensitive find
3224 * Finds the first one
3226 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3228 if ( trk && trk->name )
3229 if ( ! strcmp ( trk->name, name ) )
3235 * Get track by name - not guaranteed to be unique
3236 * Finds the first one
3238 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3240 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3244 * Get route by name - not guaranteed to be unique
3245 * Finds the first one
3247 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3249 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3252 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3254 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3255 maxmin[0].lat = trk->bbox.north;
3256 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3257 maxmin[1].lat = trk->bbox.south;
3258 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3259 maxmin[0].lon = trk->bbox.east;
3260 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3261 maxmin[1].lon = trk->bbox.west;
3264 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3266 // Continually reuse maxmin to find the latest maximum and minimum values
3267 // First set to waypoints bounds
3268 maxmin[0].lat = vtl->waypoints_bbox.north;
3269 maxmin[1].lat = vtl->waypoints_bbox.south;
3270 maxmin[0].lon = vtl->waypoints_bbox.east;
3271 maxmin[1].lon = vtl->waypoints_bbox.west;
3272 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3273 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3276 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3278 /* 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... */
3279 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3280 trw_layer_find_maxmin (vtl, maxmin);
3281 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3285 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3286 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3291 static void trw_layer_centerize ( menu_array_layer values )
3293 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3295 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3296 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3298 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3301 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3303 /* First set the center [in case previously viewing from elsewhere] */
3304 /* Then loop through zoom levels until provided positions are in view */
3305 /* This method is not particularly fast - but should work well enough */
3306 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3308 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3309 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3311 /* Convert into definite 'smallest' and 'largest' positions */
3312 struct LatLon minmin;
3313 if ( maxmin[0].lat < maxmin[1].lat )
3314 minmin.lat = maxmin[0].lat;
3316 minmin.lat = maxmin[1].lat;
3318 struct LatLon maxmax;
3319 if ( maxmin[0].lon > maxmin[1].lon )
3320 maxmax.lon = maxmin[0].lon;
3322 maxmax.lon = maxmin[1].lon;
3324 /* Never zoom in too far - generally not that useful, as too close ! */
3325 /* Always recalculate the 'best' zoom level */
3327 vik_viewport_set_zoom ( vvp, zoom );
3329 gdouble min_lat, max_lat, min_lon, max_lon;
3330 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3331 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3332 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3333 /* NB I think the logic used in this test to determine if the bounds is within view
3334 fails if track goes across 180 degrees longitude.
3335 Hopefully that situation is not too common...
3336 Mind you viking doesn't really do edge locations to well anyway */
3337 if ( min_lat < minmin.lat &&
3338 max_lat > minmin.lat &&
3339 min_lon < maxmax.lon &&
3340 max_lon > maxmax.lon )
3341 /* Found within zoom level */
3346 vik_viewport_set_zoom ( vvp, zoom );
3350 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3352 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3353 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3354 trw_layer_find_maxmin (vtl, maxmin);
3355 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3358 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3363 static void trw_layer_auto_view ( menu_array_layer values )
3365 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3366 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3367 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3368 vik_layers_panel_emit_update ( vlp );
3371 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3374 static void trw_layer_export_gpspoint ( menu_array_layer values )
3376 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3378 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3380 g_free ( auto_save_name );
3383 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3385 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3387 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3389 g_free ( auto_save_name );
3392 static void trw_layer_export_gpx ( menu_array_layer values )
3394 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3396 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3398 g_free ( auto_save_name );
3401 static void trw_layer_export_kml ( menu_array_layer values )
3403 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3405 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3407 g_free ( auto_save_name );
3410 static void trw_layer_export_geojson ( menu_array_layer values )
3412 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3414 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3416 g_free ( auto_save_name );
3419 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3421 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3422 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3425 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3427 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3430 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3432 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3435 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3437 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3439 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3440 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3442 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3444 if ( !trk || !trk->name )
3447 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3449 gchar *label = NULL;
3450 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3451 label = _("Export Route as GPX");
3453 label = _("Export Track as GPX");
3454 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3456 g_free ( auto_save_name );
3459 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3461 wpu_udata *user_data = udata;
3462 if ( wp == user_data->wp ) {
3463 user_data->uuid = id;
3469 static void trw_layer_goto_wp ( menu_array_layer values )
3471 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3472 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3473 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3474 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3475 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3477 GTK_RESPONSE_REJECT,
3479 GTK_RESPONSE_ACCEPT,
3482 GtkWidget *label, *entry;
3483 label = gtk_label_new(_("Waypoint Name:"));
3484 entry = gtk_entry_new();
3486 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3487 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3488 gtk_widget_show_all ( label );
3489 gtk_widget_show_all ( entry );
3491 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3493 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3495 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3496 // Find *first* wp with the given name
3497 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3500 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3503 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3504 vik_layers_panel_emit_update ( vlp );
3506 // Find and select on the side panel
3511 // Hmmm, want key of it
3512 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3514 if ( wpf && udata.uuid ) {
3515 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3516 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3525 gtk_widget_destroy ( dia );
3528 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3530 gchar *default_name = highest_wp_number_get(vtl);
3531 VikWaypoint *wp = vik_waypoint_new();
3532 gchar *returned_name;
3534 wp->coord = *def_coord;
3536 // Attempt to auto set height if DEM data is available
3537 vik_waypoint_apply_dem_data ( wp, TRUE );
3539 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3541 if ( returned_name )
3544 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3545 g_free (default_name);
3546 g_free (returned_name);
3549 g_free (default_name);
3550 vik_waypoint_free(wp);
3554 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3556 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3557 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3558 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3559 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3560 VikViewport *vvp = vik_window_viewport(vw);
3562 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3563 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3564 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3565 trw_layer_calculate_bounds_waypoints ( vtl );
3566 vik_layers_panel_emit_update ( vlp );
3569 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3571 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3572 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3573 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3575 trw_layer_find_maxmin (vtl, maxmin);
3576 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3577 trw_layer_calculate_bounds_waypoints ( vtl );
3578 vik_layers_panel_emit_update ( vlp );
3581 #ifdef VIK_CONFIG_GEOTAG
3582 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3584 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3586 // Update directly - not changing the mtime
3587 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3590 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3592 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3595 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3599 * Use code in separate file for this feature as reasonably complex
3601 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3603 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3604 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3605 // Unset so can be reverified later if necessary
3606 vtl->has_verified_thumbnails = FALSE;
3608 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3614 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3616 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3617 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3619 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3625 static void trw_layer_geotagging ( menu_array_layer values )
3627 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3628 // Unset so can be reverified later if necessary
3629 vtl->has_verified_thumbnails = FALSE;
3631 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3638 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3640 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3642 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3643 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3644 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3645 VikViewport *vvp = vik_window_viewport(vw);
3647 vik_datasource_mode_t mode = datasource->mode;
3648 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3649 mode = VIK_DATASOURCE_ADDTOLAYER;
3650 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3654 * Acquire into this TRW Layer straight from GPS Device
3656 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3658 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3662 * Acquire into this TRW Layer from Directions
3664 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3666 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3670 * Acquire into this TRW Layer from an entered URL
3672 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3674 trw_layer_acquire ( values, &vik_datasource_url_interface );
3677 #ifdef VIK_CONFIG_OPENSTREETMAP
3679 * Acquire into this TRW Layer from OSM
3681 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3683 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3687 * Acquire into this TRW Layer from OSM for 'My' Traces
3689 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3691 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3695 #ifdef VIK_CONFIG_GEOCACHES
3697 * Acquire into this TRW Layer from Geocaching.com
3699 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3701 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3705 #ifdef VIK_CONFIG_GEOTAG
3707 * Acquire into this TRW Layer from images
3709 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3711 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3713 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3715 // Reverify thumbnails as they may have changed
3716 vtl->has_verified_thumbnails = FALSE;
3717 trw_layer_verify_thumbnails ( vtl, NULL );
3722 * Acquire into this TRW Layer from any GPS Babel supported file
3724 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3726 trw_layer_acquire ( values, &vik_datasource_file_interface );
3729 static void trw_layer_gps_upload ( menu_array_layer values )
3731 menu_array_sublayer data;
3733 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3735 data[MA_VTL] = values[MA_VTL];
3736 data[MA_VLP] = values[MA_VLP];
3738 trw_layer_gps_upload_any ( data );
3742 * If pass_along[3] is defined that this will upload just that track
3744 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3746 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3747 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3749 // May not actually get a track here as values[2&3] can be null
3750 VikTrack *track = NULL;
3751 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3752 gboolean xfer_all = FALSE;
3754 if ( values[MA_SUBTYPE] ) {
3756 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3757 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3760 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3761 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3764 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3767 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3771 else if ( !values[MA_CONFIRM] )
3772 xfer_all = TRUE; // i.e. whole layer
3774 if (track && !track->visible) {
3775 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3779 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3780 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3781 GTK_DIALOG_DESTROY_WITH_PARENT,
3783 GTK_RESPONSE_ACCEPT,
3785 GTK_RESPONSE_REJECT,
3788 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3789 GtkWidget *response_w = NULL;
3790 #if GTK_CHECK_VERSION (2, 20, 0)
3791 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3795 gtk_widget_grab_focus ( response_w );
3797 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3799 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3800 datasource_gps_clean_up ( dgs );
3801 gtk_widget_destroy ( dialog );
3805 // Get info from reused datasource dialog widgets
3806 gchar* protocol = datasource_gps_get_protocol ( dgs );
3807 gchar* port = datasource_gps_get_descriptor ( dgs );
3808 // NB don't free the above strings as they're references to values held elsewhere
3809 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3810 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3811 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3812 gboolean turn_off = datasource_gps_get_off ( dgs );
3814 gtk_widget_destroy ( dialog );
3816 // When called from the viewport - work the corresponding layerspanel:
3818 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3821 // Apply settings to transfer to the GPS device
3828 vik_layers_panel_get_viewport (vlp),
3836 static void trw_layer_new_wp ( menu_array_layer values )
3838 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3839 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3840 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3841 instead return true if you want to update. */
3842 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 ) {
3843 trw_layer_calculate_bounds_waypoints ( vtl );
3844 vik_layers_panel_emit_update ( vlp );
3848 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3850 vtl->current_track = vik_track_new();
3851 vik_track_set_defaults ( vtl->current_track );
3852 vtl->current_track->visible = TRUE;
3853 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3854 // Create track with the preferred colour from the layer properties
3855 vtl->current_track->color = vtl->track_color;
3857 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3858 vtl->current_track->has_color = TRUE;
3859 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3862 static void trw_layer_new_track ( menu_array_layer values )
3864 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3866 if ( ! vtl->current_track ) {
3867 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3868 new_track_create_common ( vtl, name );
3871 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3875 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3877 vtl->current_track = vik_track_new();
3878 vik_track_set_defaults ( vtl->current_track );
3879 vtl->current_track->visible = TRUE;
3880 vtl->current_track->is_route = TRUE;
3881 // By default make all routes red
3882 vtl->current_track->has_color = TRUE;
3883 gdk_color_parse ( "red", &vtl->current_track->color );
3884 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3887 static void trw_layer_new_route ( menu_array_layer values )
3889 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3891 if ( ! vtl->current_track ) {
3892 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3893 new_route_create_common ( vtl, name );
3895 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3899 static void trw_layer_auto_routes_view ( menu_array_layer values )
3901 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3902 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3904 if ( g_hash_table_size (vtl->routes) > 0 ) {
3905 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3906 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3907 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3908 vik_layers_panel_emit_update ( vlp );
3913 static void trw_layer_finish_track ( menu_array_layer values )
3915 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3916 vtl->current_track = NULL;
3917 vtl->route_finder_started = FALSE;
3918 vik_layer_emit_update ( VIK_LAYER(vtl) );
3921 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3923 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3924 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3926 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3927 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3928 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3929 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3930 vik_layers_panel_emit_update ( vlp );
3934 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3936 /* NB do not care if wp is visible or not */
3937 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3940 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3942 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3943 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3945 /* Only 1 waypoint - jump straight to it */
3946 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3947 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3948 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3950 /* If at least 2 waypoints - find center and then zoom to fit */
3951 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3953 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3954 maxmin[0].lat = vtl->waypoints_bbox.north;
3955 maxmin[1].lat = vtl->waypoints_bbox.south;
3956 maxmin[0].lon = vtl->waypoints_bbox.east;
3957 maxmin[1].lon = vtl->waypoints_bbox.west;
3958 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3961 vik_layers_panel_emit_update ( vlp );
3964 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3966 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3969 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3971 if ( values[MA_MISC] ) {
3972 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3973 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3977 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3979 static menu_array_layer pass_along;
3981 GtkWidget *export_submenu;
3982 pass_along[MA_VTL] = vtl;
3983 pass_along[MA_VLP] = vlp;
3985 item = gtk_menu_item_new();
3986 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3987 gtk_widget_show ( item );
3989 if ( vtl->current_track ) {
3990 if ( vtl->current_track->is_route )
3991 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3993 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3995 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3996 gtk_widget_show ( item );
3999 item = gtk_menu_item_new ();
4000 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4001 gtk_widget_show ( item );
4004 /* Now with icons */
4005 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4006 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4007 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4008 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4009 gtk_widget_show ( item );
4011 GtkWidget *view_submenu = gtk_menu_new();
4012 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4013 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4014 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4015 gtk_widget_show ( item );
4016 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4018 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_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 _Routes") );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4025 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4026 gtk_widget_show ( item );
4028 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4030 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4031 gtk_widget_show ( item );
4033 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4034 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4036 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4037 gtk_widget_show ( item );
4039 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4041 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4042 gtk_widget_show ( item );
4044 export_submenu = gtk_menu_new ();
4045 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4046 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4047 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4048 gtk_widget_show ( item );
4049 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4051 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), 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 GPS_Mapper...") );
4057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), 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 _GPX...") );
4062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4063 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4064 gtk_widget_show ( item );
4066 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4068 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4069 gtk_widget_show ( item );
4071 if ( have_geojson_export ) {
4072 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4074 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4075 gtk_widget_show ( item );
4078 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4080 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4081 gtk_widget_show ( item );
4083 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4084 item = gtk_menu_item_new_with_mnemonic ( external1 );
4085 g_free ( external1 );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4087 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4088 gtk_widget_show ( item );
4090 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4091 item = gtk_menu_item_new_with_mnemonic ( external2 );
4092 g_free ( external2 );
4093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4094 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4095 gtk_widget_show ( item );
4097 GtkWidget *new_submenu = gtk_menu_new();
4098 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4100 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4101 gtk_widget_show(item);
4102 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4104 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4105 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4107 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4108 gtk_widget_show ( item );
4110 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4111 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4113 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4114 gtk_widget_show ( item );
4115 // Make it available only when a new track *not* already in progress
4116 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4118 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4119 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4121 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4122 gtk_widget_show ( item );
4123 // Make it available only when a new track *not* already in progress
4124 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4126 #ifdef VIK_CONFIG_GEOTAG
4127 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4128 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4129 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4130 gtk_widget_show ( item );
4133 GtkWidget *acquire_submenu = gtk_menu_new ();
4134 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4136 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4137 gtk_widget_show ( item );
4138 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4140 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4141 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4142 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4143 gtk_widget_show ( item );
4145 /* FIXME: only add menu when at least a routing engine has support for Directions */
4146 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4148 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4149 gtk_widget_show ( item );
4151 #ifdef VIK_CONFIG_OPENSTREETMAP
4152 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4155 gtk_widget_show ( item );
4157 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4159 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4160 gtk_widget_show ( item );
4163 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4165 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4166 gtk_widget_show ( item );
4168 #ifdef VIK_CONFIG_GEONAMES
4169 GtkWidget *wikipedia_submenu = gtk_menu_new();
4170 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4171 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4172 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4173 gtk_widget_show(item);
4174 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4176 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4179 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4180 gtk_widget_show ( item );
4182 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4183 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4185 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4186 gtk_widget_show ( item );
4189 #ifdef VIK_CONFIG_GEOCACHES
4190 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4192 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4193 gtk_widget_show ( item );
4196 #ifdef VIK_CONFIG_GEOTAG
4197 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4199 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4200 gtk_widget_show ( item );
4203 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4205 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4206 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4207 gtk_widget_show ( item );
4209 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4211 GtkWidget *upload_submenu = gtk_menu_new ();
4212 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4214 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4215 gtk_widget_show ( item );
4216 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4218 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4221 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4222 gtk_widget_show ( item );
4224 #ifdef VIK_CONFIG_OPENSTREETMAP
4225 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4228 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4229 gtk_widget_show ( item );
4232 GtkWidget *delete_submenu = gtk_menu_new ();
4233 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4234 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4235 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4236 gtk_widget_show ( item );
4237 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4239 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4240 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4241 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4242 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4243 gtk_widget_show ( item );
4245 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4246 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4248 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4249 gtk_widget_show ( item );
4251 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4254 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4255 gtk_widget_show ( item );
4257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4258 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4260 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4261 gtk_widget_show ( item );
4263 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4264 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4266 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4267 gtk_widget_show ( item );
4269 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4270 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4271 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4272 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4273 gtk_widget_show ( item );
4275 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4276 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4278 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4279 gtk_widget_show ( item );
4282 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4283 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4285 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4286 gtk_widget_show ( item );
4289 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4290 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4292 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4293 gtk_widget_show ( item );
4294 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4296 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4299 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4300 gtk_widget_show ( item );
4301 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4304 // Fake Waypoint UUIDs vi simple increasing integer
4305 static guint wp_uuid = 0;
4307 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4311 vik_waypoint_set_name (wp, name);
4313 if ( VIK_LAYER(vtl)->realized )
4315 // Do we need to create the sublayer:
4316 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4317 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4320 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4322 // Visibility column always needed for waypoints
4323 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 );
4325 // Actual setting of visibility dependent on the waypoint
4326 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4328 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4330 // Sort now as post_read is not called on a realized waypoint
4331 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4334 highest_wp_number_add_wp(vtl, name);
4335 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4339 // Fake Track UUIDs vi simple increasing integer
4340 static guint tr_uuid = 0;
4342 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4346 vik_track_set_name (t, name);
4348 if ( VIK_LAYER(vtl)->realized )
4350 // Do we need to create the sublayer:
4351 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4352 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4355 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4356 // Visibility column always needed for tracks
4357 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 );
4359 // Actual setting of visibility dependent on the track
4360 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4362 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4364 // Sort now as post_read is not called on a realized track
4365 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4368 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4370 trw_layer_update_treeview ( vtl, t );
4373 // Fake Route UUIDs vi simple increasing integer
4374 static guint rt_uuid = 0;
4376 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4380 vik_track_set_name (t, name);
4382 if ( VIK_LAYER(vtl)->realized )
4384 // Do we need to create the sublayer:
4385 if ( g_hash_table_size (vtl->routes) == 0 ) {
4386 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4389 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4390 // Visibility column always needed for routes
4391 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 );
4392 // Actual setting of visibility dependent on the route
4393 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4395 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4397 // Sort now as post_read is not called on a realized route
4398 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4401 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4403 trw_layer_update_treeview ( vtl, t );
4406 /* to be called whenever a track has been deleted or may have been changed. */
4407 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4409 if (vtl->current_tp_track == trk )
4410 trw_layer_cancel_current_tp ( vtl, FALSE );
4414 * Normally this is done to due the waypoint size preference having changed
4416 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4418 GHashTableIter iter;
4419 gpointer key, value;
4422 g_hash_table_iter_init ( &iter, vtl->waypoints );
4423 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4424 VikWaypoint *wp = VIK_WAYPOINT(value);
4426 // Reapply symbol setting to update the pixbuf
4427 gchar *tmp_symbol = g_strdup ( wp->symbol );
4428 vik_waypoint_set_symbol ( wp, tmp_symbol );
4429 g_free ( tmp_symbol );
4435 * trw_layer_new_unique_sublayer_name:
4437 * Allocates a unique new name
4439 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4442 gchar *newname = g_strdup(name);
4447 switch ( sublayer_type ) {
4448 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4449 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4451 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4452 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4455 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4458 // If found a name already in use try adding 1 to it and we try again
4460 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4462 newname = new_newname;
4465 } while ( id != NULL);
4470 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4472 // No more uniqueness of name forced when loading from a file
4473 // This now makes this function a little redunant as we just flow the parameters through
4474 vik_trw_layer_add_waypoint ( vtl, name, wp );
4477 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4479 if ( vtl->route_finder_append && vtl->current_track ) {
4480 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4482 // enforce end of current track equal to start of tr
4483 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4484 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4485 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4486 vik_track_add_trackpoint ( vtl->current_track,
4487 vik_trackpoint_copy ( cur_end ),
4491 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4492 vik_track_free ( tr );
4493 vtl->route_finder_append = FALSE; /* this means we have added it */
4496 // No more uniqueness of name forced when loading from a file
4498 vik_trw_layer_add_route ( vtl, name, tr );
4500 vik_trw_layer_add_track ( vtl, name, tr );
4502 if ( vtl->route_finder_check_added_track ) {
4503 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4504 vtl->route_finder_added_track = tr;
4509 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4511 *l = g_list_append(*l, id);
4515 * Move an item from one TRW layer to another TRW layer
4517 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4519 // TODO reconsider strategy when moving within layer (if anything...)
4520 gboolean rename = ( vtl_src != vtl_dest );
4524 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4525 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4529 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4531 newname = g_strdup ( trk->name );
4533 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4534 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4536 vik_trw_layer_delete_track ( vtl_src, trk );
4539 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4540 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4544 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4546 newname = g_strdup ( trk->name );
4548 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4549 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4551 vik_trw_layer_delete_route ( vtl_src, trk );
4554 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4555 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4559 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4561 newname = g_strdup ( wp->name );
4563 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4564 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4566 trw_layer_delete_waypoint ( vtl_src, wp );
4568 // Recalculate bounds even if not renamed as maybe dragged between layers
4569 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4570 trw_layer_calculate_bounds_waypoints ( vtl_src );
4574 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4576 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4577 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4579 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4580 GList *items = NULL;
4583 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4584 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4586 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4587 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4589 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4590 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4595 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4596 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4597 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4598 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4600 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4607 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4608 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4612 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4614 trku_udata *user_data = udata;
4615 if ( trk == user_data->trk ) {
4616 user_data->uuid = id;
4622 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4624 gboolean was_visible = FALSE;
4625 if ( trk && trk->name ) {
4627 if ( trk == vtl->current_track ) {
4628 vtl->current_track = NULL;
4629 vtl->current_tp_track = NULL;
4630 vtl->current_tp_id = NULL;
4631 vtl->moving_tp = FALSE;
4632 vtl->route_finder_started = FALSE;
4635 was_visible = trk->visible;
4637 if ( trk == vtl->route_finder_added_track )
4638 vtl->route_finder_added_track = NULL;
4644 // Hmmm, want key of it
4645 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4647 if ( trkf && udata.uuid ) {
4648 /* could be current_tp, so we have to check */
4649 trw_layer_cancel_tps_of_track ( vtl, trk );
4651 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4654 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4655 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4656 g_hash_table_remove ( vtl->tracks, udata.uuid );
4658 // If last sublayer, then remove sublayer container
4659 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4660 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4663 // Incase it was selected (no item delete signal ATM)
4664 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4670 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4672 gboolean was_visible = FALSE;
4674 if ( trk && trk->name ) {
4676 if ( trk == vtl->current_track ) {
4677 vtl->current_track = NULL;
4678 vtl->current_tp_track = NULL;
4679 vtl->current_tp_id = NULL;
4680 vtl->moving_tp = FALSE;
4683 was_visible = trk->visible;
4685 if ( trk == vtl->route_finder_added_track )
4686 vtl->route_finder_added_track = NULL;
4692 // Hmmm, want key of it
4693 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4695 if ( trkf && udata.uuid ) {
4696 /* could be current_tp, so we have to check */
4697 trw_layer_cancel_tps_of_track ( vtl, trk );
4699 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4702 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4703 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4704 g_hash_table_remove ( vtl->routes, udata.uuid );
4706 // If last sublayer, then remove sublayer container
4707 if ( g_hash_table_size (vtl->routes) == 0 ) {
4708 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4711 // Incase it was selected (no item delete signal ATM)
4712 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4718 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4720 gboolean was_visible = FALSE;
4722 if ( wp && wp->name ) {
4724 if ( wp == vtl->current_wp ) {
4725 vtl->current_wp = NULL;
4726 vtl->current_wp_id = NULL;
4727 vtl->moving_wp = FALSE;
4730 was_visible = wp->visible;
4736 // Hmmm, want key of it
4737 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4739 if ( wpf && udata.uuid ) {
4740 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4743 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4744 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4746 highest_wp_number_remove_wp(vtl, wp->name);
4747 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4749 // If last sublayer, then remove sublayer container
4750 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4751 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4754 // Incase it was selected (no item delete signal ATM)
4755 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4763 // Only for temporary use by trw_layer_delete_waypoint_by_name
4764 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4766 wpu_udata *user_data = udata;
4767 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4768 user_data->uuid = id;
4775 * Delete a waypoint by the given name
4776 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4777 * as there be multiple waypoints with the same name
4779 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4782 // Fake a waypoint with the given name
4783 udata.wp = vik_waypoint_new ();
4784 vik_waypoint_set_name (udata.wp, name);
4785 // Currently only the name is used in this waypoint find function
4788 // Hmmm, want key of it
4789 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4791 vik_waypoint_free (udata.wp);
4793 if ( wpf && udata.uuid )
4794 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4800 VikTrack *trk; // input
4801 gpointer uuid; // output
4804 // Only for temporary use by trw_layer_delete_track_by_name
4805 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4807 tpu_udata *user_data = udata;
4808 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4809 user_data->uuid = id;
4816 * Delete a track by the given name
4817 * NOTE: ATM this will delete the first encountered Track with the specified name
4818 * as there may be multiple tracks with the same name within the specified hash table
4820 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4823 // Fake a track with the given name
4824 udata.trk = vik_track_new ();
4825 vik_track_set_name (udata.trk, name);
4826 // Currently only the name is used in this waypoint find function
4829 // Hmmm, want key of it
4830 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4832 vik_track_free (udata.trk);
4834 if ( trkf && udata.uuid ) {
4835 // This could be a little better written...
4836 if ( vtl->tracks == ht_tracks )
4837 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4838 if ( vtl->routes == ht_tracks )
4839 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4846 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4848 vik_treeview_item_delete (vt, it );
4851 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4854 vtl->current_track = NULL;
4855 vtl->route_finder_added_track = NULL;
4856 if (vtl->current_tp_track)
4857 trw_layer_cancel_current_tp(vtl, FALSE);
4859 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4860 g_hash_table_remove_all(vtl->routes_iters);
4861 g_hash_table_remove_all(vtl->routes);
4863 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4865 vik_layer_emit_update ( VIK_LAYER(vtl) );
4868 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4871 vtl->current_track = NULL;
4872 vtl->route_finder_added_track = NULL;
4873 if (vtl->current_tp_track)
4874 trw_layer_cancel_current_tp(vtl, FALSE);
4876 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4877 g_hash_table_remove_all(vtl->tracks_iters);
4878 g_hash_table_remove_all(vtl->tracks);
4880 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4882 vik_layer_emit_update ( VIK_LAYER(vtl) );
4885 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4887 vtl->current_wp = NULL;
4888 vtl->current_wp_id = NULL;
4889 vtl->moving_wp = FALSE;
4891 highest_wp_number_reset(vtl);
4893 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4894 g_hash_table_remove_all(vtl->waypoints_iters);
4895 g_hash_table_remove_all(vtl->waypoints);
4897 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4899 vik_layer_emit_update ( VIK_LAYER(vtl) );
4902 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4904 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4905 // Get confirmation from the user
4906 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4907 _("Are you sure you want to delete all tracks in %s?"),
4908 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4909 vik_trw_layer_delete_all_tracks (vtl);
4912 static void trw_layer_delete_all_routes ( menu_array_layer values )
4914 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4915 // Get confirmation from the user
4916 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4917 _("Are you sure you want to delete all routes in %s?"),
4918 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4919 vik_trw_layer_delete_all_routes (vtl);
4922 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4924 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4925 // Get confirmation from the user
4926 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4927 _("Are you sure you want to delete all waypoints in %s?"),
4928 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4929 vik_trw_layer_delete_all_waypoints (vtl);
4932 static void trw_layer_delete_item ( menu_array_sublayer values )
4934 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4935 gboolean was_visible = FALSE;
4936 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4938 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4939 if ( wp && wp->name ) {
4940 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4941 // Get confirmation from the user
4942 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4943 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4944 _("Are you sure you want to delete the waypoint \"%s\"?"),
4947 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4948 trw_layer_calculate_bounds_waypoints ( vtl );
4951 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4953 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4954 if ( trk && trk->name ) {
4955 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4956 // Get confirmation from the user
4957 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4958 _("Are you sure you want to delete the track \"%s\"?"),
4961 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4966 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4967 if ( trk && trk->name ) {
4968 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4969 // Get confirmation from the user
4970 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4971 _("Are you sure you want to delete the route \"%s\"?"),
4974 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4978 vik_layer_emit_update ( VIK_LAYER(vtl) );
4982 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4984 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4986 vik_waypoint_set_name ( wp, new_name );
4988 // Now update the treeview as well
4993 // Need key of it for treeview update
4994 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4996 if ( wpf && udataU.uuid ) {
4997 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5000 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
5001 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
5007 * Maintain icon of waypoint in the treeview
5009 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5011 // update the treeview
5016 // Need key of it for treeview update
5017 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5019 if ( wpf && udataU.uuid ) {
5020 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5023 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5028 static void trw_layer_properties_item ( menu_array_sublayer values )
5030 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5031 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5033 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5035 if ( wp && wp->name )
5037 gboolean updated = FALSE;
5038 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5040 trw_layer_waypoint_rename ( vtl, wp, new_name );
5042 if ( updated && values[MA_TV_ITER] )
5043 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5045 if ( updated && VIK_LAYER(vtl)->visible )
5046 vik_layer_emit_update ( VIK_LAYER(vtl) );
5052 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5053 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5055 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5057 if ( tr && tr->name )
5059 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5070 * trw_layer_track_statistics:
5072 * Show track statistics.
5073 * ATM jump to the stats page in the properties
5074 * TODO: consider separating the stats into an individual dialog?
5076 static void trw_layer_track_statistics ( menu_array_sublayer values )
5078 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5080 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5081 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5083 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5085 if ( trk && trk->name ) {
5086 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5096 * Update the treeview of the track id - primarily to update the icon
5098 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5104 gpointer *trkf = NULL;
5105 if ( trk->is_route )
5106 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5108 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5110 if ( trkf && udata.uuid ) {
5112 GtkTreeIter *iter = NULL;
5113 if ( trk->is_route )
5114 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5116 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5119 // TODO: Make this a function
5120 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5121 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5122 ((trk->color.green & 0xff00) << 8) |
5123 (trk->color.blue & 0xff00);
5124 gdk_pixbuf_fill ( pixbuf, pixel );
5125 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5126 g_object_unref (pixbuf);
5133 Parameter 1 -> VikLayersPanel
5134 Parameter 2 -> VikLayer
5135 Parameter 3 -> VikViewport
5137 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5140 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5141 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5144 /* since vlp not set, vl & vvp should be valid instead! */
5146 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5147 vik_layer_emit_update ( VIK_LAYER(vl) );
5152 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5154 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5156 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5157 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5159 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5161 if ( track && track->trackpoints )
5162 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5165 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5167 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5169 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5170 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5172 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5174 if ( track && track->trackpoints )
5176 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5178 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5179 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5180 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5181 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5182 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5186 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5188 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5190 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5191 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5193 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5198 // Converting a track to a route can be a bit more complicated,
5199 // so give a chance to change our minds:
5200 if ( !trk->is_route &&
5201 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5202 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5204 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5205 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5210 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5213 trk_copy->is_route = !trk_copy->is_route;
5215 // ATM can't set name to self - so must create temporary copy
5216 gchar *name = g_strdup ( trk_copy->name );
5218 // Delete old one and then add new one
5219 if ( trk->is_route ) {
5220 vik_trw_layer_delete_route ( vtl, trk );
5221 vik_trw_layer_add_track ( vtl, name, trk_copy );
5224 // Extra route conversion bits...
5225 vik_track_merge_segments ( trk_copy );
5226 vik_track_to_routepoints ( trk_copy );
5228 vik_trw_layer_delete_track ( vtl, trk );
5229 vik_trw_layer_add_route ( vtl, name, trk_copy );
5233 // Update in case color of track / route changes when moving between sublayers
5234 vik_layer_emit_update ( VIK_LAYER(vtl) );
5237 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5239 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5241 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5242 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5244 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5247 vik_track_anonymize_times ( track );
5250 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5252 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5254 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5255 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5257 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5262 vtl->current_track = track;
5263 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);
5265 if ( track->trackpoints )
5266 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5270 * extend a track using route finder
5272 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5274 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5275 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5279 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5280 vtl->current_track = track;
5281 vtl->route_finder_started = TRUE;
5283 if ( track->trackpoints )
5284 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5290 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5292 // If have a vlp then perform a basic test to see if any DEM info available...
5294 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5296 if ( !g_list_length(dems) ) {
5297 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5305 * apply_dem_data_common:
5307 * A common function for applying the DEM values and reporting the results.
5309 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5311 if ( !trw_layer_dem_test ( vtl, vlp ) )
5314 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5315 // Inform user how much was changed
5317 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5318 g_snprintf(str, 64, tmp_str, changed);
5319 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5322 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5324 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5326 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5327 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5329 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5332 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5335 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5337 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5339 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5340 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5342 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5345 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5351 * A common function for applying the elevation smoothing and reporting the results.
5353 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5355 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5356 // Inform user how much was changed
5358 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5359 g_snprintf(str, 64, tmp_str, changed);
5360 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5366 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5368 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5370 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5371 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5373 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5378 smooth_it ( vtl, track, FALSE );
5381 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5383 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5385 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5386 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5388 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5393 smooth_it ( vtl, track, TRUE );
5397 * Commonal helper function
5399 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5402 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5403 g_snprintf(str, 64, tmp_str, changed);
5404 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5407 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5409 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5410 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5412 if ( !trw_layer_dem_test ( vtl, vlp ) )
5416 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5418 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5420 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5424 GHashTableIter iter;
5425 gpointer key, value;
5427 g_hash_table_iter_init ( &iter, vtl->waypoints );
5428 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5429 VikWaypoint *wp = VIK_WAYPOINT(value);
5430 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5433 wp_changed_message ( vtl, changed );
5436 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5438 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5439 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5441 if ( !trw_layer_dem_test ( vtl, vlp ) )
5445 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5447 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5449 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5453 GHashTableIter iter;
5454 gpointer key, value;
5456 g_hash_table_iter_init ( &iter, vtl->waypoints );
5457 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5458 VikWaypoint *wp = VIK_WAYPOINT(value);
5459 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5462 wp_changed_message ( vtl, changed );
5465 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5467 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5469 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5470 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5472 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5476 if ( !track->trackpoints )
5478 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5481 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5483 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5485 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5486 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5488 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5493 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5496 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5499 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5501 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5503 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5504 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5506 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5511 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5514 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5517 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5519 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5521 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5522 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5524 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5529 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5532 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5536 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5538 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5540 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5542 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5543 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5545 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5547 if ( trk && trk->trackpoints )
5549 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5550 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5551 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5552 if ( values[MA_VLP] )
5553 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5555 vik_layer_emit_update ( VIK_LAYER(vtl) );
5560 * Refine the selected track/route with a routing engine.
5561 * The routing engine is selected by the user, when requestiong the job.
5563 static void trw_layer_route_refine ( menu_array_sublayer values )
5565 static gint last_engine = 0;
5566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5569 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5570 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5572 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5574 if ( trk && trk->trackpoints )
5576 /* Check size of the route */
5577 int nb = vik_track_get_tp_count(trk);
5579 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5580 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5581 GTK_MESSAGE_WARNING,
5582 GTK_BUTTONS_OK_CANCEL,
5583 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5585 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5586 gtk_widget_destroy ( dialog );
5587 if (response != GTK_RESPONSE_OK )
5590 /* Select engine from dialog */
5591 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5592 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5593 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5595 GTK_RESPONSE_REJECT,
5597 GTK_RESPONSE_ACCEPT,
5599 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5600 gtk_widget_show_all(label);
5602 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5604 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5605 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5606 gtk_widget_show_all(combo);
5608 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5610 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5612 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5614 /* Dialog validated: retrieve selected engine and do the job */
5615 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5616 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5619 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5621 /* Force saving track */
5622 /* FIXME: remove or rename this hack */
5623 vtl->route_finder_check_added_track = TRUE;
5626 vik_routing_engine_refine (routing, vtl, trk);
5628 /* FIXME: remove or rename this hack */
5629 if ( vtl->route_finder_added_track )
5630 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5632 vtl->route_finder_added_track = NULL;
5633 vtl->route_finder_check_added_track = FALSE;
5635 vik_layer_emit_update ( VIK_LAYER(vtl) );
5637 /* Restore cursor */
5638 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5640 gtk_widget_destroy ( dialog );
5644 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5646 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5647 trw_layer_tpwin_init ( vtl );
5650 /*************************************
5651 * merge/split by time routines
5652 *************************************/
5654 /* called for each key in track hash table.
5655 * If the current track has the same time stamp type, add it to the result,
5656 * except the one pointed by "exclude".
5657 * set exclude to NULL if there is no exclude to check.
5658 * Note that the result is in reverse (for performance reasons).
5663 gboolean with_timestamps;
5665 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5667 twt_udata *user_data = udata;
5668 VikTrackpoint *p1, *p2;
5669 VikTrack *trk = VIK_TRACK(value);
5670 if (trk == user_data->exclude) {
5674 if (trk->trackpoints) {
5675 p1 = vik_track_get_tp_first(trk);
5676 p2 = vik_track_get_tp_last(trk);
5678 if ( user_data->with_timestamps ) {
5679 if (!p1->has_timestamp || !p2->has_timestamp) {
5684 // Don't add tracks with timestamps when getting non timestamp tracks
5685 if (p1->has_timestamp || p2->has_timestamp) {
5691 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5695 * find_nearby_tracks_by_time:
5697 * Called for each track in track hash table.
5698 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5699 * to the current track, then the current track is added to the list in user_data[0]
5701 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5703 VikTrack *trk = VIK_TRACK(value);
5705 GList **nearby_tracks = ((gpointer *)user_data)[0];
5706 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5708 if ( !orig_trk || !orig_trk->trackpoints )
5712 * detect reasons for not merging, and return
5713 * if no reason is found not to merge, then do it.
5716 twt_udata *udata = user_data;
5717 // Exclude the original track from the compiled list
5718 if (trk == udata->exclude) {
5722 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5723 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5725 if (trk->trackpoints) {
5727 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5728 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5730 if (!p1->has_timestamp || !p2->has_timestamp) {
5731 //g_print("no timestamp\n");
5735 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5736 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5737 if (! (abs(t1 - p2->timestamp) < threshold ||
5739 abs(p1->timestamp - t2) < threshold)
5746 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5749 /* comparison function used to sort tracks; a and b are hash table keys */
5750 /* Not actively used - can be restored if needed
5751 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5753 GHashTable *tracks = user_data;
5756 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5757 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5759 if (t1 < t2) return -1;
5760 if (t1 > t2) return 1;
5765 /* comparison function used to sort trackpoints */
5766 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5768 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5770 if (t1 < t2) return -1;
5771 if (t1 > t2) return 1;
5776 * comparison function which can be used to sort tracks or waypoints by name
5778 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5780 const gchar* namea = (const gchar*) a;
5781 const gchar* nameb = (const gchar*) b;
5782 if ( namea == NULL || nameb == NULL)
5785 // Same sort method as used in the vik_treeview_*_alphabetize functions
5786 return strcmp ( namea, nameb );
5790 * Attempt to merge selected track with other tracks specified by the user
5791 * Tracks to merge with must be of the same 'type' as the selected track -
5792 * either all with timestamps, or all without timestamps
5794 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5796 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5797 GList *other_tracks = NULL;
5798 GHashTable *ght_tracks;
5799 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5800 ght_tracks = vtl->routes;
5802 ght_tracks = vtl->tracks;
5804 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5809 if ( !track->trackpoints )
5813 udata.result = &other_tracks;
5814 udata.exclude = track;
5815 // Allow merging with 'similar' time type time tracks
5816 // i.e. either those times, or those without
5817 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5819 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5820 other_tracks = g_list_reverse(other_tracks);
5822 if ( !other_tracks ) {
5823 if ( udata.with_timestamps )
5824 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5826 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5830 // Sort alphabetically for user presentation
5831 // Convert into list of names for usage with dialog function
5832 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5833 GList *other_tracks_names = NULL;
5834 GList *iter = g_list_first ( other_tracks );
5836 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5837 iter = g_list_next ( iter );
5840 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5842 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5846 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5847 g_list_free(other_tracks);
5848 g_list_free(other_tracks_names);
5853 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5854 VikTrack *merge_track;
5855 if ( track->is_route )
5856 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5858 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5861 vik_track_steal_and_append_trackpoints ( track, merge_track );
5862 if ( track->is_route )
5863 vik_trw_layer_delete_route (vtl, merge_track);
5865 vik_trw_layer_delete_track (vtl, merge_track);
5866 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5869 for (l = merge_list; l != NULL; l = g_list_next(l))
5871 g_list_free(merge_list);
5873 vik_layer_emit_update( VIK_LAYER(vtl) );
5877 // c.f. trw_layer_sorted_track_id_by_name_list
5878 // but don't add the specified track to the list (normally current track)
5879 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5881 twt_udata *user_data = udata;
5884 if (trk == user_data->exclude) {
5888 // Sort named list alphabetically
5889 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5893 * Join - this allows combining 'tracks' and 'track routes'
5894 * i.e. doesn't care about whether tracks have consistent timestamps
5895 * ATM can only append one track at a time to the currently selected track
5897 static void trw_layer_append_track ( menu_array_sublayer values )
5900 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5902 GHashTable *ght_tracks;
5903 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5904 ght_tracks = vtl->routes;
5906 ght_tracks = vtl->tracks;
5908 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5913 GList *other_tracks_names = NULL;
5915 // Sort alphabetically for user presentation
5916 // Convert into list of names for usage with dialog function
5917 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5919 udata.result = &other_tracks_names;
5920 udata.exclude = trk;
5922 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5924 // Note the limit to selecting one track only
5925 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5926 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5927 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5930 trk->is_route ? _("Append Route"): _("Append Track"),
5931 trk->is_route ? _("Select the route to append after the current route") :
5932 _("Select the track to append after the current track") );
5934 g_list_free(other_tracks_names);
5936 // It's a list, but shouldn't contain more than one other track!
5937 if ( append_list ) {
5939 for (l = append_list; l != NULL; l = g_list_next(l)) {
5940 // TODO: at present this uses the first track found by name,
5941 // which with potential multiple same named tracks may not be the one selected...
5942 VikTrack *append_track;
5943 if ( trk->is_route )
5944 append_track = vik_trw_layer_get_route ( vtl, l->data );
5946 append_track = vik_trw_layer_get_track ( vtl, l->data );
5948 if ( append_track ) {
5949 vik_track_steal_and_append_trackpoints ( trk, append_track );
5950 if ( trk->is_route )
5951 vik_trw_layer_delete_route (vtl, append_track);
5953 vik_trw_layer_delete_track (vtl, append_track);
5956 for (l = append_list; l != NULL; l = g_list_next(l))
5958 g_list_free(append_list);
5960 vik_layer_emit_update( VIK_LAYER(vtl) );
5965 * Very similar to trw_layer_append_track for joining
5966 * but this allows selection from the 'other' list
5967 * If a track is selected, then is shows routes and joins the selected one
5968 * If a route is selected, then is shows tracks and joins the selected one
5970 static void trw_layer_append_other ( menu_array_sublayer values )
5973 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5975 GHashTable *ght_mykind, *ght_others;
5976 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5977 ght_mykind = vtl->routes;
5978 ght_others = vtl->tracks;
5981 ght_mykind = vtl->tracks;
5982 ght_others = vtl->routes;
5985 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5990 GList *other_tracks_names = NULL;
5992 // Sort alphabetically for user presentation
5993 // Convert into list of names for usage with dialog function
5994 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5996 udata.result = &other_tracks_names;
5997 udata.exclude = trk;
5999 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6001 // Note the limit to selecting one track only
6002 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6003 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6004 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6007 trk->is_route ? _("Append Track"): _("Append Route"),
6008 trk->is_route ? _("Select the track to append after the current route") :
6009 _("Select the route to append after the current track") );
6011 g_list_free(other_tracks_names);
6013 // It's a list, but shouldn't contain more than one other track!
6014 if ( append_list ) {
6016 for (l = append_list; l != NULL; l = g_list_next(l)) {
6017 // TODO: at present this uses the first track found by name,
6018 // which with potential multiple same named tracks may not be the one selected...
6020 // Get FROM THE OTHER TYPE list
6021 VikTrack *append_track;
6022 if ( trk->is_route )
6023 append_track = vik_trw_layer_get_track ( vtl, l->data );
6025 append_track = vik_trw_layer_get_route ( vtl, l->data );
6027 if ( append_track ) {
6029 if ( !append_track->is_route &&
6030 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6031 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6033 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6034 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6035 vik_track_merge_segments ( append_track );
6036 vik_track_to_routepoints ( append_track );
6043 vik_track_steal_and_append_trackpoints ( trk, append_track );
6045 // Delete copied which is FROM THE OTHER TYPE list
6046 if ( trk->is_route )
6047 vik_trw_layer_delete_track (vtl, append_track);
6049 vik_trw_layer_delete_route (vtl, append_track);
6052 for (l = append_list; l != NULL; l = g_list_next(l))
6054 g_list_free(append_list);
6055 vik_layer_emit_update( VIK_LAYER(vtl) );
6059 /* merge by segments */
6060 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6062 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6063 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6064 guint segments = vik_track_merge_segments ( trk );
6065 // NB currently no need to redraw as segments not actually shown on the display
6066 // However inform the user of what happened:
6068 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6069 g_snprintf(str, 64, tmp_str, segments);
6070 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6073 /* merge by time routine */
6074 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6076 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6080 GList *tracks_with_timestamp = NULL;
6081 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6082 if (orig_trk->trackpoints &&
6083 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6084 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6089 udata.result = &tracks_with_timestamp;
6090 udata.exclude = orig_trk;
6091 udata.with_timestamps = TRUE;
6092 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6093 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6095 if (!tracks_with_timestamp) {
6096 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6099 g_list_free(tracks_with_timestamp);
6101 static guint threshold_in_minutes = 1;
6102 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6103 _("Merge Threshold..."),
6104 _("Merge when time between tracks less than:"),
6105 &threshold_in_minutes)) {
6109 // keep attempting to merge all tracks until no merges within the time specified is possible
6110 gboolean attempt_merge = TRUE;
6111 GList *nearby_tracks = NULL;
6113 static gpointer params[3];
6115 while ( attempt_merge ) {
6117 // Don't try again unless tracks have changed
6118 attempt_merge = FALSE;
6120 trps = orig_trk->trackpoints;
6124 if (nearby_tracks) {
6125 g_list_free(nearby_tracks);
6126 nearby_tracks = NULL;
6129 params[0] = &nearby_tracks;
6130 params[1] = orig_trk;
6131 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6133 /* get a list of adjacent-in-time tracks */
6134 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6137 GList *l = nearby_tracks;
6139 /* remove trackpoints from merged track, delete track */
6140 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6141 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6143 // Tracks have changed, therefore retry again against all the remaining tracks
6144 attempt_merge = TRUE;
6149 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6152 g_list_free(nearby_tracks);
6154 vik_layer_emit_update( VIK_LAYER(vtl) );
6158 * Split a track at the currently selected trackpoint
6160 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6162 if ( !vtl->current_tpl )
6165 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6166 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6168 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6169 GList *newglist = g_list_alloc ();
6170 newglist->prev = NULL;
6171 newglist->next = vtl->current_tpl->next;
6172 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6173 tr->trackpoints = newglist;
6175 vtl->current_tpl->next->prev = newglist; /* end old track here */
6176 vtl->current_tpl->next = NULL;
6178 // Bounds of the selected track changed due to the split
6179 vik_track_calculate_bounds ( vtl->current_tp_track );
6181 vtl->current_tpl = newglist; /* change tp to first of new track. */
6182 vtl->current_tp_track = tr;
6185 vik_trw_layer_add_route ( vtl, name, tr );
6187 vik_trw_layer_add_track ( vtl, name, tr );
6189 // Bounds of the new track created by the split
6190 vik_track_calculate_bounds ( tr );
6196 // Also need id of newly created track
6199 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6201 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6203 if ( trkf && udata.uuid )
6204 vtl->current_tp_id = udata.uuid;
6206 vtl->current_tp_id = NULL;
6208 vik_layer_emit_update(VIK_LAYER(vtl));
6214 /* split by time routine */
6215 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6217 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6218 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6219 GList *trps = track->trackpoints;
6221 GList *newlists = NULL;
6222 GList *newtps = NULL;
6223 static guint thr = 1;
6230 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6231 _("Split Threshold..."),
6232 _("Split when time between trackpoints exceeds:"),
6237 /* iterate through trackpoints, and copy them into new lists without touching original list */
6238 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6242 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6244 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6247 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6248 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6249 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6251 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6256 if (ts - prev_ts > thr*60) {
6257 /* flush accumulated trackpoints into new list */
6258 newlists = g_list_append(newlists, g_list_reverse(newtps));
6262 /* accumulate trackpoint copies in newtps, in reverse order */
6263 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6265 iter = g_list_next(iter);
6268 newlists = g_list_append(newlists, g_list_reverse(newtps));
6271 /* put lists of trackpoints into tracks */
6273 // Only bother updating if the split results in new tracks
6274 if (g_list_length (newlists) > 1) {
6279 tr = vik_track_copy ( track, FALSE );
6280 tr->trackpoints = (GList *)(iter->data);
6282 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6283 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6284 g_free ( new_tr_name );
6285 vik_track_calculate_bounds ( tr );
6286 iter = g_list_next(iter);
6288 // Remove original track and then update the display
6289 vik_trw_layer_delete_track (vtl, track);
6290 vik_layer_emit_update(VIK_LAYER(vtl));
6292 g_list_free(newlists);
6296 * Split a track by the number of points as specified by the user
6298 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6300 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6302 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6303 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6305 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6310 // Check valid track
6311 GList *trps = track->trackpoints;
6315 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6316 _("Split Every Nth Point"),
6317 _("Split on every Nth point:"),
6318 250, // Default value as per typical limited track capacity of various GPS devices
6322 // Was a valid number returned?
6328 GList *newlists = NULL;
6329 GList *newtps = NULL;
6334 /* accumulate trackpoint copies in newtps, in reverse order */
6335 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6337 if (count >= points) {
6338 /* flush accumulated trackpoints into new list */
6339 newlists = g_list_append(newlists, g_list_reverse(newtps));
6343 iter = g_list_next(iter);
6346 // If there is a remaining chunk put that into the new split list
6347 // This may well be the whole track if no split points were encountered
6349 newlists = g_list_append(newlists, g_list_reverse(newtps));
6352 /* put lists of trackpoints into tracks */
6354 // Only bother updating if the split results in new tracks
6355 if (g_list_length (newlists) > 1) {
6360 tr = vik_track_copy ( track, FALSE );
6361 tr->trackpoints = (GList *)(iter->data);
6363 if ( track->is_route ) {
6364 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6365 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6368 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6369 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6371 g_free ( new_tr_name );
6372 vik_track_calculate_bounds ( tr );
6374 iter = g_list_next(iter);
6376 // Remove original track and then update the display
6377 if ( track->is_route )
6378 vik_trw_layer_delete_route (vtl, track);
6380 vik_trw_layer_delete_track (vtl, track);
6381 vik_layer_emit_update(VIK_LAYER(vtl));
6383 g_list_free(newlists);
6387 * Split a track at the currently selected trackpoint
6389 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6391 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6392 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6393 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6397 * Split a track by its segments
6398 * Routes do not have segments so don't call this for routes
6400 static void trw_layer_split_segments ( menu_array_sublayer values )
6402 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6403 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6410 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6413 for ( i = 0; i < ntracks; i++ ) {
6415 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6416 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6417 g_free ( new_tr_name );
6422 // Remove original track
6423 vik_trw_layer_delete_track ( vtl, trk );
6424 vik_layer_emit_update ( VIK_LAYER(vtl) );
6427 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6430 /* end of split/merge routines */
6432 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6436 // Find available adjacent trackpoint
6437 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6438 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6439 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6441 // Delete current trackpoint
6442 vik_trackpoint_free ( vtl->current_tpl->data );
6443 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6445 // Set to current to the available adjacent trackpoint
6446 vtl->current_tpl = new_tpl;
6448 if ( vtl->current_tp_track ) {
6449 vik_track_calculate_bounds ( vtl->current_tp_track );
6453 // Delete current trackpoint
6454 vik_trackpoint_free ( vtl->current_tpl->data );
6455 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6456 trw_layer_cancel_current_tp ( vtl, FALSE );
6461 * Delete the selected point
6463 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6465 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6467 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6468 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6470 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6475 if ( !vtl->current_tpl )
6478 trw_layer_trackpoint_selected_delete ( vtl, trk );
6480 // Track has been updated so update tps:
6481 trw_layer_cancel_tps_of_track ( vtl, trk );
6483 vik_layer_emit_update ( VIK_LAYER(vtl) );
6487 * Delete adjacent track points at the same position
6488 * AKA Delete Dulplicates on the Properties Window
6490 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6492 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6494 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6495 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6497 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6502 gulong removed = vik_track_remove_dup_points ( trk );
6504 // Track has been updated so update tps:
6505 trw_layer_cancel_tps_of_track ( vtl, trk );
6507 // Inform user how much was deleted as it's not obvious from the normal view
6509 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6510 g_snprintf(str, 64, tmp_str, removed);
6511 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6513 vik_layer_emit_update ( VIK_LAYER(vtl) );
6517 * Delete adjacent track points with the same timestamp
6518 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6520 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6522 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6524 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6525 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6527 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6532 gulong removed = vik_track_remove_same_time_points ( trk );
6534 // Track has been updated so update tps:
6535 trw_layer_cancel_tps_of_track ( vtl, trk );
6537 // Inform user how much was deleted as it's not obvious from the normal view
6539 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6540 g_snprintf(str, 64, tmp_str, removed);
6541 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6543 vik_layer_emit_update ( VIK_LAYER(vtl) );
6549 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6551 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6553 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6554 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6556 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6561 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6563 vik_layer_emit_update ( VIK_LAYER(vtl) );
6566 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6568 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6570 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6571 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6573 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6578 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6580 vik_layer_emit_update ( VIK_LAYER(vtl) );
6586 static void trw_layer_reverse ( menu_array_sublayer values )
6588 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6590 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6591 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6593 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6598 vik_track_reverse ( track );
6600 vik_layer_emit_update ( VIK_LAYER(vtl) );
6604 * Open a diary at the specified date
6606 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6609 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6610 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6611 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6612 g_error_free ( err );
6618 * Open a diary at the date of the track or waypoint
6620 static void trw_layer_diary ( menu_array_sublayer values )
6622 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6624 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6625 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6631 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6632 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6633 trw_layer_diary_open ( vtl, date_buf );
6636 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6638 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6639 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6645 if ( wpt->has_timestamp ) {
6646 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6647 trw_layer_diary_open ( vtl, date_buf );
6650 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6655 * Similar to trw_layer_enum_item, but this uses a sorted method
6658 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6660 GList **list = (GList**)udata;
6661 // *list = g_list_prepend(*all, key); //unsorted method
6662 // Sort named list alphabetically
6663 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6668 * Now Waypoint specific sort
6670 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6672 GList **list = (GList**)udata;
6673 // Sort named list alphabetically
6674 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6678 * Track specific sort
6680 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6682 GList **list = (GList**)udata;
6683 // Sort named list alphabetically
6684 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6689 gboolean has_same_track_name;
6690 const gchar *same_track_name;
6691 } same_track_name_udata;
6693 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6695 const gchar* namea = (const gchar*) aa;
6696 const gchar* nameb = (const gchar*) bb;
6699 gint result = strcmp ( namea, nameb );
6701 if ( result == 0 ) {
6702 // Found two names the same
6703 same_track_name_udata *user_data = udata;
6704 user_data->has_same_track_name = TRUE;
6705 user_data->same_track_name = namea;
6708 // Leave ordering the same
6713 * Find out if any tracks have the same name in this hash table
6715 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6717 // Sort items by name, then compare if any next to each other are the same
6719 GList *track_names = NULL;
6720 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6723 if ( ! track_names )
6726 same_track_name_udata udata;
6727 udata.has_same_track_name = FALSE;
6729 // Use sort routine to traverse list comparing items
6730 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6731 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6732 // Still no tracks...
6736 return udata.has_same_track_name;
6740 * Force unqiue track names for the track table specified
6741 * Note the panel is a required parameter to enable the update of the names displayed
6742 * Specify if on tracks or else on routes
6744 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6746 // . Search list for an instance of repeated name
6747 // . get track of this name
6748 // . create new name
6749 // . rename track & update equiv. treeview iter
6750 // . repeat until all different
6752 same_track_name_udata udata;
6754 GList *track_names = NULL;
6755 udata.has_same_track_name = FALSE;
6756 udata.same_track_name = NULL;
6758 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6761 if ( ! track_names )
6764 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6766 // Still no tracks...
6767 if ( ! dummy_list1 )
6770 while ( udata.has_same_track_name ) {
6772 // Find a track with the same name
6775 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6777 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6781 g_critical("Houston, we've had a problem.");
6782 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6783 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6788 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6789 vik_track_set_name ( trk, newname );
6795 // Need want key of it for treeview update
6796 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6798 if ( trkf && udataU.uuid ) {
6802 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6804 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6807 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6809 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6811 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6815 // Start trying to find same names again...
6817 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6818 udata.has_same_track_name = FALSE;
6819 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6821 // No tracks any more - give up searching
6822 if ( ! dummy_list2 )
6823 udata.has_same_track_name = FALSE;
6827 vik_layers_panel_emit_update ( vlp );
6830 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6832 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6835 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6836 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6837 iter = &(vtl->tracks_iter);
6838 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6840 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6841 iter = &(vtl->routes_iter);
6842 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6844 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6845 iter = &(vtl->waypoints_iter);
6846 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6850 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6853 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6855 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6858 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6859 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6860 iter = &(vtl->tracks_iter);
6861 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6863 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6864 iter = &(vtl->routes_iter);
6865 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6867 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6868 iter = &(vtl->waypoints_iter);
6869 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6873 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6879 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6881 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6884 // Ensure list of track names offered is unique
6885 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6886 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6887 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6888 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6894 // Sort list alphabetically for better presentation
6895 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6898 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6902 // Get list of items to delete from the user
6903 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6906 _("Delete Selection"),
6907 _("Select tracks to delete"));
6910 // Delete requested tracks
6911 // since specificly requested, IMHO no need for extra confirmation
6912 if ( delete_list ) {
6914 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6915 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6916 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6918 g_list_free(delete_list);
6919 vik_layer_emit_update( VIK_LAYER(vtl) );
6926 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6928 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6931 // Ensure list of track names offered is unique
6932 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6933 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6934 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6935 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6941 // Sort list alphabetically for better presentation
6942 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6945 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6949 // Get list of items to delete from the user
6950 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6953 _("Delete Selection"),
6954 _("Select routes to delete") );
6957 // Delete requested routes
6958 // since specificly requested, IMHO no need for extra confirmation
6959 if ( delete_list ) {
6961 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6962 // This deletes first route it finds of that name (but uniqueness is enforced above)
6963 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6965 g_list_free(delete_list);
6966 vik_layer_emit_update( VIK_LAYER(vtl) );
6971 gboolean has_same_waypoint_name;
6972 const gchar *same_waypoint_name;
6973 } same_waypoint_name_udata;
6975 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6977 const gchar* namea = (const gchar*) aa;
6978 const gchar* nameb = (const gchar*) bb;
6981 gint result = strcmp ( namea, nameb );
6983 if ( result == 0 ) {
6984 // Found two names the same
6985 same_waypoint_name_udata *user_data = udata;
6986 user_data->has_same_waypoint_name = TRUE;
6987 user_data->same_waypoint_name = namea;
6990 // Leave ordering the same
6995 * Find out if any waypoints have the same name in this layer
6997 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6999 // Sort items by name, then compare if any next to each other are the same
7001 GList *waypoint_names = NULL;
7002 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7005 if ( ! waypoint_names )
7008 same_waypoint_name_udata udata;
7009 udata.has_same_waypoint_name = FALSE;
7011 // Use sort routine to traverse list comparing items
7012 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7013 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7014 // Still no waypoints...
7018 return udata.has_same_waypoint_name;
7022 * Force unqiue waypoint names for this layer
7023 * Note the panel is a required parameter to enable the update of the names displayed
7025 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7027 // . Search list for an instance of repeated name
7028 // . get waypoint of this name
7029 // . create new name
7030 // . rename waypoint & update equiv. treeview iter
7031 // . repeat until all different
7033 same_waypoint_name_udata udata;
7035 GList *waypoint_names = NULL;
7036 udata.has_same_waypoint_name = FALSE;
7037 udata.same_waypoint_name = NULL;
7039 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7042 if ( ! waypoint_names )
7045 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7047 // Still no waypoints...
7048 if ( ! dummy_list1 )
7051 while ( udata.has_same_waypoint_name ) {
7053 // Find a waypoint with the same name
7054 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7058 g_critical("Houston, we've had a problem.");
7059 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7060 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7065 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7067 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7069 // Start trying to find same names again...
7070 waypoint_names = NULL;
7071 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7072 udata.has_same_waypoint_name = FALSE;
7073 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7075 // No waypoints any more - give up searching
7076 if ( ! dummy_list2 )
7077 udata.has_same_waypoint_name = FALSE;
7081 vik_layers_panel_emit_update ( vlp );
7087 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7089 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7092 // Ensure list of waypoint names offered is unique
7093 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7094 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7095 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7096 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7102 // Sort list alphabetically for better presentation
7103 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7105 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7109 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7111 // Get list of items to delete from the user
7112 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7115 _("Delete Selection"),
7116 _("Select waypoints to delete"));
7119 // Delete requested waypoints
7120 // since specificly requested, IMHO no need for extra confirmation
7121 if ( delete_list ) {
7123 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7124 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7125 trw_layer_delete_waypoint_by_name (vtl, l->data);
7127 g_list_free(delete_list);
7129 trw_layer_calculate_bounds_waypoints ( vtl );
7130 vik_layer_emit_update( VIK_LAYER(vtl) );
7138 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7140 vik_treeview_item_toggle_visible ( vt, it );
7146 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7148 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7154 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7156 wp->visible = GPOINTER_TO_INT (on_off);
7162 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7164 wp->visible = !wp->visible;
7170 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7172 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7173 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7174 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7175 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7177 vik_layer_emit_update ( VIK_LAYER(vtl) );
7183 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7185 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7186 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7187 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7188 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7190 vik_layer_emit_update ( VIK_LAYER(vtl) );
7196 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7198 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7199 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7200 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7202 vik_layer_emit_update ( VIK_LAYER(vtl) );
7208 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7210 trk->visible = GPOINTER_TO_INT (on_off);
7216 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7218 trk->visible = !trk->visible;
7224 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7226 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7227 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7228 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7229 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7231 vik_layer_emit_update ( VIK_LAYER(vtl) );
7237 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7239 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7240 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7241 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7242 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7244 vik_layer_emit_update ( VIK_LAYER(vtl) );
7250 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7252 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7253 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7254 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7256 vik_layer_emit_update ( VIK_LAYER(vtl) );
7262 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7264 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7265 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7266 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7267 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7269 vik_layer_emit_update ( VIK_LAYER(vtl) );
7275 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7277 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7278 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7279 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7280 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7282 vik_layer_emit_update ( VIK_LAYER(vtl) );
7288 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7290 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7291 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7292 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7294 vik_layer_emit_update ( VIK_LAYER(vtl) );
7298 * vik_trw_layer_build_waypoint_list_t:
7300 * Helper function to construct a list of #vik_trw_waypoint_list_t
7302 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7304 GList *waypoints_and_layers = NULL;
7305 // build waypoints_and_layers list
7306 while ( waypoints ) {
7307 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7308 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7310 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7311 waypoints = g_list_next ( waypoints );
7313 return waypoints_and_layers;
7317 * trw_layer_create_waypoint_list:
7319 * Create the latest list of waypoints with the associated layer(s)
7320 * Although this will always be from a single layer here
7322 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7324 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7325 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7327 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7331 * trw_layer_analyse_close:
7333 * Stuff to do on dialog closure
7335 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7337 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7338 gtk_widget_destroy ( dialog );
7339 vtl->tracks_analysis_dialog = NULL;
7343 * vik_trw_layer_build_track_list_t:
7345 * Helper function to construct a list of #vik_trw_track_list_t
7347 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7349 GList *tracks_and_layers = NULL;
7350 // build tracks_and_layers list
7352 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7353 vtdl->trk = VIK_TRACK(tracks->data);
7355 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7356 tracks = g_list_next ( tracks );
7358 return tracks_and_layers;
7362 * trw_layer_create_track_list:
7364 * Create the latest list of tracks with the associated layer(s)
7365 * Although this will always be from a single layer here
7367 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7369 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7370 GList *tracks = NULL;
7371 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7372 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7374 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7376 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7379 static void trw_layer_tracks_stats ( menu_array_layer values )
7381 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7382 // There can only be one!
7383 if ( vtl->tracks_analysis_dialog )
7386 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7387 VIK_LAYER(vtl)->name,
7389 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7390 trw_layer_create_track_list,
7391 trw_layer_analyse_close );
7397 static void trw_layer_routes_stats ( menu_array_layer values )
7399 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7400 // There can only be one!
7401 if ( vtl->tracks_analysis_dialog )
7404 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7405 VIK_LAYER(vtl)->name,
7407 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7408 trw_layer_create_track_list,
7409 trw_layer_analyse_close );
7412 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7414 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7415 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7417 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7420 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7422 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7423 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7426 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7427 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7431 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7433 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7434 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7438 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7439 } else if ( !strncmp(wp->comment, "http", 4) ) {
7440 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7441 } else if ( !strncmp(wp->description, "http", 4) ) {
7442 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7446 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7448 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7450 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7452 // No actual change to the name supplied
7454 if (strcmp(newname, wp->name) == 0 )
7457 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7460 // An existing waypoint has been found with the requested name
7461 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7462 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7467 // Update WP name and refresh the treeview
7468 vik_waypoint_set_name (wp, newname);
7470 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7471 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7473 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7478 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7480 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7482 // No actual change to the name supplied
7484 if (strcmp(newname, trk->name) == 0)
7487 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7490 // An existing track has been found with the requested name
7491 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7492 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7496 // Update track name and refresh GUI parts
7497 vik_track_set_name (trk, newname);
7499 // Update any subwindows that could be displaying this track which has changed name
7500 // Only one Track Edit Window
7501 if ( l->current_tp_track == trk && l->tpwin ) {
7502 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7504 // Property Dialog of the track
7505 vik_trw_layer_propwin_update ( trk );
7507 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7508 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7510 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7515 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7517 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7519 // No actual change to the name supplied
7521 if (strcmp(newname, trk->name) == 0)
7524 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7527 // An existing track has been found with the requested name
7528 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7529 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7533 // Update track name and refresh GUI parts
7534 vik_track_set_name (trk, newname);
7536 // Update any subwindows that could be displaying this track which has changed name
7537 // Only one Track Edit Window
7538 if ( l->current_tp_track == trk && l->tpwin ) {
7539 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7541 // Property Dialog of the track
7542 vik_trw_layer_propwin_update ( trk );
7544 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7545 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7547 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7554 static gboolean is_valid_geocache_name ( gchar *str )
7556 gint len = strlen ( str );
7557 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]));
7560 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7562 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7563 a_acquire_set_filter_track ( trk );
7566 #ifdef VIK_CONFIG_GOOGLE
7567 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7569 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7570 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7573 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7575 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7577 gchar *escaped = uri_escape ( tr->comment );
7578 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7579 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7586 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7587 /* viewpoint is now available instead */
7588 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7590 static menu_array_sublayer pass_along;
7592 gboolean rv = FALSE;
7594 pass_along[MA_VTL] = l;
7595 pass_along[MA_VLP] = vlp;
7596 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7597 pass_along[MA_SUBLAYER_ID] = sublayer;
7598 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7599 pass_along[MA_VVP] = vvp;
7600 pass_along[MA_TV_ITER] = iter;
7601 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7603 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7607 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7608 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7609 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7610 gtk_widget_show ( item );
7612 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7613 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7614 if (tr && tr->property_dialog)
7615 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7617 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7618 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7619 if (tr && tr->property_dialog)
7620 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7623 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_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_COPY, NULL );
7629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7630 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7631 gtk_widget_show ( item );
7633 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7634 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7635 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7636 gtk_widget_show ( item );
7638 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7640 // Always create separator as now there is always at least the transform menu option
7641 item = gtk_menu_item_new ();
7642 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7643 gtk_widget_show ( item );
7645 /* could be a right-click using the tool */
7646 if ( vlp != NULL ) {
7647 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7648 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7649 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7650 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7651 gtk_widget_show ( item );
7654 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7656 if ( wp && wp->name ) {
7657 if ( is_valid_geocache_name ( wp->name ) ) {
7658 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7659 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7660 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7661 gtk_widget_show ( item );
7663 #ifdef VIK_CONFIG_GEOTAG
7664 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7665 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7666 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7667 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7668 gtk_widget_show ( item );
7672 if ( wp && wp->image )
7674 // Set up image paramater
7675 pass_along[MA_MISC] = wp->image;
7677 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7678 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
7679 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7680 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7681 gtk_widget_show ( item );
7683 #ifdef VIK_CONFIG_GEOTAG
7684 GtkWidget *geotag_submenu = gtk_menu_new ();
7685 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7686 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7687 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7688 gtk_widget_show ( item );
7689 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7691 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7693 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7694 gtk_widget_show ( item );
7696 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7697 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7698 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7699 gtk_widget_show ( item );
7706 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7707 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7708 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7709 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7711 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7712 gtk_widget_show ( item );
7718 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7719 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7720 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7721 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7722 gtk_widget_show ( item );
7723 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7724 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7725 gtk_widget_set_sensitive ( item, TRUE );
7727 gtk_widget_set_sensitive ( item, FALSE );
7730 item = gtk_menu_item_new ();
7731 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7732 gtk_widget_show ( item );
7735 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7738 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7741 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7742 gtk_widget_show ( item );
7745 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7747 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7748 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7749 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7750 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7751 gtk_widget_show ( item );
7753 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7754 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7756 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7757 gtk_widget_show ( item );
7759 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7761 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7762 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7763 gtk_widget_show ( item );
7765 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7766 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7767 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7768 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7769 gtk_widget_show ( item );
7771 GtkWidget *vis_submenu = gtk_menu_new ();
7772 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7773 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7774 gtk_widget_show ( item );
7775 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7777 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7778 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7780 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7781 gtk_widget_show ( item );
7783 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7784 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7786 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7787 gtk_widget_show ( item );
7789 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7790 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7791 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7792 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7793 gtk_widget_show ( item );
7795 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7796 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7798 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7801 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7805 if ( l->current_track && !l->current_track->is_route ) {
7806 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7807 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7808 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7809 gtk_widget_show ( item );
7811 item = gtk_menu_item_new ();
7812 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7813 gtk_widget_show ( item );
7816 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7819 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7820 gtk_widget_show ( item );
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7825 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7826 gtk_widget_show ( item );
7827 // Make it available only when a new track *not* already in progress
7828 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7830 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7831 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7832 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7833 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7834 gtk_widget_show ( item );
7836 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7837 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7838 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7839 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7840 gtk_widget_show ( item );
7842 GtkWidget *vis_submenu = gtk_menu_new ();
7843 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7844 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7845 gtk_widget_show ( item );
7846 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7848 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7849 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7850 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7851 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7852 gtk_widget_show ( item );
7854 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7855 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7856 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7857 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7858 gtk_widget_show ( item );
7860 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7863 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7865 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7866 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7868 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7869 gtk_widget_show ( item );
7871 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7873 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7874 gtk_widget_show ( item );
7877 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7881 if ( l->current_track && l->current_track->is_route ) {
7882 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7883 // Reuse finish track method
7884 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7885 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7886 gtk_widget_show ( item );
7888 item = gtk_menu_item_new ();
7889 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7890 gtk_widget_show ( item );
7893 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7896 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7897 gtk_widget_show ( item );
7899 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7902 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7903 gtk_widget_show ( item );
7904 // Make it available only when a new track *not* already in progress
7905 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7907 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7908 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7909 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7910 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7911 gtk_widget_show ( item );
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7916 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7917 gtk_widget_show ( item );
7919 GtkWidget *vis_submenu = gtk_menu_new ();
7920 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7921 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7922 gtk_widget_show ( item );
7923 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7925 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7926 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7929 gtk_widget_show ( item );
7931 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7932 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7934 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7935 gtk_widget_show ( item );
7937 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7940 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7942 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7943 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7945 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7947 gtk_widget_show ( item );
7949 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7951 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7952 gtk_widget_show ( item );
7956 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7957 GtkWidget *submenu_sort = gtk_menu_new ();
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7959 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7961 gtk_widget_show ( item );
7962 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7964 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7965 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7966 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7967 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7968 gtk_widget_show ( item );
7970 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7973 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7974 gtk_widget_show ( item );
7977 GtkWidget *upload_submenu = gtk_menu_new ();
7979 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7981 item = gtk_menu_item_new ();
7982 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7983 gtk_widget_show ( item );
7985 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7986 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7987 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7988 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7989 if ( l->current_track ) {
7990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7991 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7992 gtk_widget_show ( item );
7995 item = gtk_menu_item_new ();
7996 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7997 gtk_widget_show ( item );
8000 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8001 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
8003 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
8004 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8006 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8007 gtk_widget_show ( item );
8009 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8011 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8012 gtk_widget_show ( item );
8014 GtkWidget *goto_submenu;
8015 goto_submenu = gtk_menu_new ();
8016 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8018 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8019 gtk_widget_show ( item );
8020 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8022 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8023 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8025 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8026 gtk_widget_show ( item );
8028 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8031 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8032 gtk_widget_show ( item );
8034 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8035 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8037 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8038 gtk_widget_show ( item );
8040 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8043 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8044 gtk_widget_show ( item );
8046 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8049 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8050 gtk_widget_show ( item );
8052 // Routes don't have speeds
8053 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8054 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8055 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8057 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8058 gtk_widget_show ( item );
8061 GtkWidget *combine_submenu;
8062 combine_submenu = gtk_menu_new ();
8063 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8064 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8065 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8066 gtk_widget_show ( item );
8067 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8069 // Routes don't have times or segments...
8070 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8071 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8073 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8074 gtk_widget_show ( item );
8076 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8078 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8079 gtk_widget_show ( item );
8082 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8084 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8085 gtk_widget_show ( item );
8087 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8088 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8090 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8092 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8093 gtk_widget_show ( item );
8095 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8096 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8098 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8100 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8101 gtk_widget_show ( item );
8103 GtkWidget *split_submenu;
8104 split_submenu = gtk_menu_new ();
8105 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8107 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8108 gtk_widget_show ( item );
8109 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8111 // Routes don't have times or segments...
8112 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8113 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8115 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8116 gtk_widget_show ( item );
8118 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8119 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8121 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8122 gtk_widget_show ( item );
8125 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8127 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8128 gtk_widget_show ( item );
8130 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8132 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8133 gtk_widget_show ( item );
8134 // Make it available only when a trackpoint is selected.
8135 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8137 GtkWidget *insert_submenu = gtk_menu_new ();
8138 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8140 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8141 gtk_widget_show ( item );
8142 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8144 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8145 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8146 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8147 gtk_widget_show ( item );
8148 // Make it available only when a point is selected
8149 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8151 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8153 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8154 gtk_widget_show ( item );
8155 // Make it available only when a point is selected
8156 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8158 GtkWidget *delete_submenu;
8159 delete_submenu = gtk_menu_new ();
8160 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8162 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8163 gtk_widget_show ( item );
8164 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8166 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8167 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8169 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8170 gtk_widget_show ( item );
8171 // Make it available only when a point is selected
8172 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8174 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8176 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8177 gtk_widget_show ( item );
8179 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8181 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8182 gtk_widget_show ( item );
8184 GtkWidget *transform_submenu;
8185 transform_submenu = gtk_menu_new ();
8186 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8188 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8189 gtk_widget_show ( item );
8190 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8192 GtkWidget *dem_submenu;
8193 dem_submenu = gtk_menu_new ();
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8195 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
8196 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8197 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8199 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8201 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8202 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8203 gtk_widget_show ( item );
8205 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8207 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8208 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8209 gtk_widget_show ( item );
8211 GtkWidget *smooth_submenu;
8212 smooth_submenu = gtk_menu_new ();
8213 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8214 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8215 gtk_widget_show ( item );
8216 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8218 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8219 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8220 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8221 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8222 gtk_widget_show ( item );
8224 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8225 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8226 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8227 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8228 gtk_widget_show ( item );
8230 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8231 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8233 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8234 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8236 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8237 gtk_widget_show ( item );
8239 // Routes don't have timestamps - so this is only available for tracks
8240 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8241 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8243 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8244 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8245 gtk_widget_show ( item );
8248 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8249 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8251 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8255 gtk_widget_show ( item );
8257 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8258 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8262 gtk_widget_show ( item );
8265 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8267 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8268 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8270 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8271 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
8272 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8273 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8274 gtk_widget_show ( item );
8277 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8278 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8280 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8281 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8283 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8284 gtk_widget_show ( item );
8286 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8287 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8289 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8290 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8292 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8293 gtk_widget_show ( item );
8295 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8296 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8297 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
8298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8299 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8300 gtk_widget_show ( item );
8303 // ATM can't upload a single waypoint but can do waypoints to a GPS
8304 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8305 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8306 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8307 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8308 gtk_widget_show ( item );
8309 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8311 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8312 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8314 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8315 gtk_widget_show ( item );
8319 // Only made available if a suitable program is installed
8320 if ( have_diary_program ) {
8321 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8322 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8323 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8324 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8325 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8326 gtk_widget_show ( item );
8330 #ifdef VIK_CONFIG_GOOGLE
8331 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8333 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8334 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8335 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8336 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8337 gtk_widget_show ( item );
8341 // Some things aren't usable with routes
8342 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8343 #ifdef VIK_CONFIG_OPENSTREETMAP
8344 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8345 // Convert internal pointer into track
8346 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8348 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8349 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8350 gtk_widget_show ( item );
8353 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8355 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8356 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8357 gtk_widget_show ( item );
8359 /* ATM This function is only available via the layers panel, due to needing a vlp */
8361 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8362 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8363 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8365 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8366 gtk_widget_show ( item );
8370 #ifdef VIK_CONFIG_GEOTAG
8371 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8372 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8373 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8374 gtk_widget_show ( item );
8378 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8379 // Only show on viewport popmenu when a trackpoint is selected
8380 if ( ! vlp && l->current_tpl ) {
8382 item = gtk_menu_item_new ();
8383 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8384 gtk_widget_show ( item );
8386 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8387 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8388 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8389 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8390 gtk_widget_show ( item );
8394 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8395 GtkWidget *transform_submenu;
8396 transform_submenu = gtk_menu_new ();
8397 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8398 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8399 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8400 gtk_widget_show ( item );
8401 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8403 GtkWidget *dem_submenu;
8404 dem_submenu = gtk_menu_new ();
8405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8406 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
8407 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8408 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8410 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8412 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8413 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8414 gtk_widget_show ( item );
8416 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8417 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8418 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8419 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8420 gtk_widget_show ( item );
8423 gtk_widget_show_all ( GTK_WIDGET(menu) );
8428 // TODO: Probably better to rework this track manipulation in viktrack.c
8429 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8432 if (!vtl->current_tpl)
8435 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8436 VikTrackpoint *tp_other = NULL;
8439 if (!vtl->current_tpl->prev)
8441 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8443 if (!vtl->current_tpl->next)
8445 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8448 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8451 VikTrackpoint *tp_new = vik_trackpoint_new();
8452 struct LatLon ll_current, ll_other;
8453 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8454 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8456 /* main positional interpolation */
8457 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8458 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8460 /* Now other properties that can be interpolated */
8461 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8463 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8464 /* Note here the division is applied to each part, then added
8465 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8466 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8467 tp_new->has_timestamp = TRUE;
8470 if (tp_current->speed != NAN && tp_other->speed != NAN)
8471 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8473 /* TODO - improve interpolation of course, as it may not be correct.
8474 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8475 [similar applies if value is in radians] */
8476 if (tp_current->course != NAN && tp_other->course != NAN)
8477 tp_new->course = (tp_current->course + tp_other->course)/2;
8479 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8481 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8482 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8484 // Otherwise try routes
8485 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8489 gint index = g_list_index ( trk->trackpoints, tp_current );
8493 // NB no recalculation of bounds since it is inserted between points
8494 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8499 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8505 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8509 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8511 if ( vtl->current_tpl )
8513 vtl->current_tpl = NULL;
8514 vtl->current_tp_track = NULL;
8515 vtl->current_tp_id = NULL;
8516 vik_layer_emit_update(VIK_LAYER(vtl));
8520 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8522 VikTrack *trk = vtl->current_tp_track;
8524 // Notional center of a track is simply an average of the bounding box extremities
8525 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8526 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er );
8527 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8530 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8532 g_assert ( vtl->tpwin != NULL );
8533 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8534 trw_layer_cancel_current_tp ( vtl, TRUE );
8536 if ( vtl->current_tpl == NULL )
8539 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8541 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8542 my_tpwin_set_tp ( vtl );
8544 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8546 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8548 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8552 trw_layer_trackpoint_selected_delete ( vtl, tr );
8554 if ( vtl->current_tpl )
8555 // Reset dialog with the available adjacent trackpoint
8556 my_tpwin_set_tp ( vtl );
8558 vik_layer_emit_update(VIK_LAYER(vtl));
8560 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8562 if ( vtl->current_tp_track ) {
8563 vtl->current_tpl = vtl->current_tpl->next;
8564 my_tpwin_set_tp ( vtl );
8566 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8568 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8570 if ( vtl->current_tp_track ) {
8571 vtl->current_tpl = vtl->current_tpl->prev;
8572 my_tpwin_set_tp ( vtl );
8574 vik_layer_emit_update(VIK_LAYER(vtl));
8576 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8578 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8579 vik_layer_emit_update(VIK_LAYER(vtl));
8581 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8582 vik_layer_emit_update(VIK_LAYER(vtl));
8586 * trw_layer_dialog_shift:
8587 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8589 * Try to reposition a dialog if it's over the specified coord
8590 * so to not obscure the item of interest
8592 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8594 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8596 // Attempt force dialog to be shown so we can find out where it is more reliably...
8597 while ( gtk_events_pending() )
8598 gtk_main_iteration ();
8600 // get parent window position & size
8601 gint win_pos_x, win_pos_y;
8602 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8604 gint win_size_x, win_size_y;
8605 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8607 // get own dialog size
8608 gint dia_size_x, dia_size_y;
8609 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8611 // get own dialog position
8612 gint dia_pos_x, dia_pos_y;
8613 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8615 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8616 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8618 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8620 gint vp_xx, vp_yy; // In viewport pixels
8621 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8623 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8627 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8629 // Transform Viewport pixels into absolute pixels
8630 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8631 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8633 // Is dialog over the point (to within an ^^ edge value)
8634 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8635 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8639 gint hh = vik_viewport_get_height ( vvp );
8641 // Consider the difference in viewport to the full window
8642 gint offset_y = dest_y;
8643 // Add difference between dialog and window sizes
8644 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8646 if ( vp_yy > hh/2 ) {
8647 // Point in bottom half, move window to top half
8648 gtk_window_move ( dialog, dia_pos_x, offset_y );
8651 // Point in top half, move dialog down
8652 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8656 // Shift left<->right
8657 gint ww = vik_viewport_get_width ( vvp );
8659 // Consider the difference in viewport to the full window
8660 gint offset_x = dest_x;
8661 // Add difference between dialog and window sizes
8662 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8664 if ( vp_xx > ww/2 ) {
8665 // Point on right, move window to left
8666 gtk_window_move ( dialog, offset_x, dia_pos_y );
8669 // Point on left, move right
8670 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8678 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8682 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8683 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8684 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8685 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8687 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8689 if ( vtl->current_tpl ) {
8690 // get tp pixel position
8691 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8693 // Shift up<->down to try not to obscure the trackpoint.
8694 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8698 if ( vtl->current_tpl )
8699 if ( vtl->current_tp_track )
8700 my_tpwin_set_tp ( vtl );
8701 /* set layer name and TP data */
8704 /***************************************************************************
8706 ***************************************************************************/
8708 /*** Utility data structures and functions ****/
8712 gint closest_x, closest_y;
8713 gboolean draw_images;
8714 gpointer *closest_wp_id;
8715 VikWaypoint *closest_wp;
8721 gint closest_x, closest_y;
8722 gpointer closest_track_id;
8723 VikTrackpoint *closest_tp;
8729 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8735 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8737 // If waypoint has an image then use the image size to select
8738 if ( params->draw_images && wp->image ) {
8739 gint slackx, slacky;
8740 slackx = wp->image_width / 2;
8741 slacky = wp->image_height / 2;
8743 if ( x <= params->x + slackx && x >= params->x - slackx
8744 && y <= params->y + slacky && y >= params->y - slacky ) {
8745 params->closest_wp_id = id;
8746 params->closest_wp = wp;
8747 params->closest_x = x;
8748 params->closest_y = y;
8751 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8752 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8753 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8755 params->closest_wp_id = id;
8756 params->closest_wp = wp;
8757 params->closest_x = x;
8758 params->closest_y = y;
8762 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8764 GList *tpl = t->trackpoints;
8770 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8776 tp = VIK_TRACKPOINT(tpl->data);
8778 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8780 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8781 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8782 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8784 params->closest_track_id = id;
8785 params->closest_tp = tp;
8786 params->closest_tpl = tpl;
8787 params->closest_x = x;
8788 params->closest_y = y;
8794 // ATM: Leave this as 'Track' only.
8795 // Not overly bothered about having a snap to route trackpoint capability
8796 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8798 TPSearchParams params;
8802 params.closest_track_id = NULL;
8803 params.closest_tp = NULL;
8804 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8805 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8806 return params.closest_tp;
8809 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8811 WPSearchParams params;
8815 params.draw_images = vtl->drawimages;
8816 params.closest_wp = NULL;
8817 params.closest_wp_id = NULL;
8818 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8819 return params.closest_wp;
8823 // Some forward declarations
8824 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8825 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8826 static void marker_end_move ( tool_ed_t *t );
8829 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
8833 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8835 // Here always allow snapping back to the original location
8836 // this is useful when one decides not to move the thing afterall
8837 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8840 if ( event->state & GDK_CONTROL_MASK )
8842 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8844 new_coord = tp->coord;
8848 if ( event->state & GDK_SHIFT_MASK )
8850 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8852 new_coord = wp->coord;
8856 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8858 marker_moveto ( t, x, y );
8865 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8867 if ( t->holding && event->button == 1 )
8869 // Prevent accidental (small) shifts when specific movement has not been requested
8870 // (as the click release has occurred within the click object detection area)
8875 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8878 if ( event->state & GDK_CONTROL_MASK )
8880 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8882 new_coord = tp->coord;
8886 if ( event->state & GDK_SHIFT_MASK )
8888 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8890 new_coord = wp->coord;
8893 marker_end_move ( t );
8895 // Determine if working on a waypoint or a trackpoint
8896 if ( t->is_waypoint ) {
8897 // Update waypoint position
8898 vtl->current_wp->coord = new_coord;
8899 trw_layer_calculate_bounds_waypoints ( vtl );
8900 // Reset waypoint pointer
8901 vtl->current_wp = NULL;
8902 vtl->current_wp_id = NULL;
8905 if ( vtl->current_tpl ) {
8906 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8908 if ( vtl->current_tp_track )
8909 vik_track_calculate_bounds ( vtl->current_tp_track );
8912 if ( vtl->current_tp_track )
8913 my_tpwin_set_tp ( vtl );
8914 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8918 vik_layer_emit_update ( VIK_LAYER(vtl) );
8925 Returns true if a waypoint or track is found near the requested event position for this particular layer
8926 The item found is automatically selected
8927 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8929 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8931 if ( event->button != 1 )
8934 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8937 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8941 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8943 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8945 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8946 WPSearchParams wp_params;
8947 wp_params.vvp = vvp;
8948 wp_params.x = event->x;
8949 wp_params.y = event->y;
8950 wp_params.draw_images = vtl->drawimages;
8951 wp_params.closest_wp_id = NULL;
8952 wp_params.closest_wp = NULL;
8954 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8956 if ( wp_params.closest_wp ) {
8959 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8961 // Too easy to move it so must be holding shift to start immediately moving it
8962 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8963 if ( event->state & GDK_SHIFT_MASK ||
8964 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8965 // Put into 'move buffer'
8966 // NB vvp & vw already set in tet
8967 tet->vtl = (gpointer)vtl;
8968 tet->is_waypoint = TRUE;
8970 marker_begin_move (tet, event->x, event->y);
8973 vtl->current_wp = wp_params.closest_wp;
8974 vtl->current_wp_id = wp_params.closest_wp_id;
8976 if ( event->type == GDK_2BUTTON_PRESS ) {
8977 if ( vtl->current_wp->image ) {
8978 menu_array_sublayer values;
8979 values[MA_VTL] = vtl;
8980 values[MA_MISC] = vtl->current_wp->image;
8981 trw_layer_show_picture ( values );
8985 vik_layer_emit_update ( VIK_LAYER(vtl) );
8991 // Used for both track and route lists
8992 TPSearchParams tp_params;
8993 tp_params.vvp = vvp;
8994 tp_params.x = event->x;
8995 tp_params.y = event->y;
8996 tp_params.closest_track_id = NULL;
8997 tp_params.closest_tp = NULL;
8998 tp_params.closest_tpl = NULL;
8999 tp_params.bbox = bbox;
9001 if (vtl->tracks_visible) {
9002 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9004 if ( tp_params.closest_tp ) {
9006 // Always select + highlight the track
9007 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9009 tet->is_waypoint = FALSE;
9011 // Select the Trackpoint
9012 // Can move it immediately when control held or it's the previously selected tp
9013 if ( event->state & GDK_CONTROL_MASK ||
9014 vtl->current_tpl == tp_params.closest_tpl ) {
9015 // Put into 'move buffer'
9016 // NB vvp & vw already set in tet
9017 tet->vtl = (gpointer)vtl;
9018 marker_begin_move (tet, event->x, event->y);
9021 vtl->current_tpl = tp_params.closest_tpl;
9022 vtl->current_tp_id = tp_params.closest_track_id;
9023 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9025 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9028 my_tpwin_set_tp ( vtl );
9030 vik_layer_emit_update ( VIK_LAYER(vtl) );
9035 // Try again for routes
9036 if (vtl->routes_visible) {
9037 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9039 if ( tp_params.closest_tp ) {
9041 // Always select + highlight the track
9042 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9044 tet->is_waypoint = FALSE;
9046 // Select the Trackpoint
9047 // Can move it immediately when control held or it's the previously selected tp
9048 if ( event->state & GDK_CONTROL_MASK ||
9049 vtl->current_tpl == tp_params.closest_tpl ) {
9050 // Put into 'move buffer'
9051 // NB vvp & vw already set in tet
9052 tet->vtl = (gpointer)vtl;
9053 marker_begin_move (tet, event->x, event->y);
9056 vtl->current_tpl = tp_params.closest_tpl;
9057 vtl->current_tp_id = tp_params.closest_track_id;
9058 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9060 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9063 my_tpwin_set_tp ( vtl );
9065 vik_layer_emit_update ( VIK_LAYER(vtl) );
9070 /* these aren't the droids you're looking for */
9071 vtl->current_wp = NULL;
9072 vtl->current_wp_id = NULL;
9073 trw_layer_cancel_current_tp ( vtl, FALSE );
9076 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9081 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9083 if ( event->button != 3 )
9086 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9089 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9092 /* Post menu for the currently selected item */
9094 /* See if a track is selected */
9095 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9096 if ( track && track->visible ) {
9098 if ( track->name ) {
9100 if ( vtl->track_right_click_menu )
9101 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9103 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9110 if ( track->is_route )
9111 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9113 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9115 if ( trkf && udataU.uuid ) {
9118 if ( track->is_route )
9119 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9121 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9123 trw_layer_sublayer_add_menu_items ( vtl,
9124 vtl->track_right_click_menu,
9126 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9132 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9138 /* See if a waypoint is selected */
9139 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9140 if ( waypoint && waypoint->visible ) {
9141 if ( waypoint->name ) {
9143 if ( vtl->wp_right_click_menu )
9144 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9146 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9149 udata.wp = waypoint;
9152 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9154 if ( wpf && udata.uuid ) {
9155 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9157 trw_layer_sublayer_add_menu_items ( vtl,
9158 vtl->wp_right_click_menu,
9160 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9165 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9174 /* background drawing hook, to be passed the viewport */
9175 static gboolean tool_sync_done = TRUE;
9177 static gboolean tool_sync(gpointer data)
9179 VikViewport *vvp = data;
9180 gdk_threads_enter();
9181 vik_viewport_sync(vvp);
9182 tool_sync_done = TRUE;
9183 gdk_threads_leave();
9187 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9190 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9191 gdk_gc_set_function ( t->gc, GDK_INVERT );
9192 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9193 vik_viewport_sync(t->vvp);
9199 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9201 VikViewport *vvp = t->vvp;
9202 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9203 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9208 if (tool_sync_done) {
9209 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9210 tool_sync_done = FALSE;
9214 static void marker_end_move ( tool_ed_t *t )
9216 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9217 g_object_unref ( t->gc );
9222 /*** Edit waypoint ****/
9224 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9226 tool_ed_t *t = g_new(tool_ed_t, 1);
9232 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9237 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9239 WPSearchParams params;
9240 tool_ed_t *t = data;
9241 VikViewport *vvp = t->vvp;
9243 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9250 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9253 if ( vtl->current_wp && vtl->current_wp->visible )
9255 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9257 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9259 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9260 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9262 if ( event->button == 3 )
9263 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9265 marker_begin_move(t, event->x, event->y);
9272 params.x = event->x;
9273 params.y = event->y;
9274 params.draw_images = vtl->drawimages;
9275 params.closest_wp_id = NULL;
9276 params.closest_wp = NULL;
9277 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9278 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9280 if ( event->button == 3 )
9281 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9283 marker_begin_move(t, event->x, event->y);
9286 else if ( params.closest_wp )
9288 if ( event->button == 3 )
9289 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9291 vtl->waypoint_rightclick = FALSE;
9293 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9295 vtl->current_wp = params.closest_wp;
9296 vtl->current_wp_id = params.closest_wp_id;
9298 /* could make it so don't update if old WP is off screen and new is null but oh well */
9299 vik_layer_emit_update ( VIK_LAYER(vtl) );
9303 vtl->current_wp = NULL;
9304 vtl->current_wp_id = NULL;
9305 vtl->waypoint_rightclick = FALSE;
9306 vik_layer_emit_update ( VIK_LAYER(vtl) );
9310 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9312 tool_ed_t *t = data;
9313 VikViewport *vvp = t->vvp;
9315 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9320 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9323 if ( event->state & GDK_CONTROL_MASK )
9325 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9327 new_coord = tp->coord;
9331 if ( event->state & GDK_SHIFT_MASK )
9333 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9334 if ( wp && wp != vtl->current_wp )
9335 new_coord = wp->coord;
9340 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9342 marker_moveto ( t, x, y );
9349 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9351 tool_ed_t *t = data;
9352 VikViewport *vvp = t->vvp;
9354 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9357 if ( t->holding && event->button == 1 )
9360 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9363 if ( event->state & GDK_CONTROL_MASK )
9365 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9367 new_coord = tp->coord;
9371 if ( event->state & GDK_SHIFT_MASK )
9373 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9374 if ( wp && wp != vtl->current_wp )
9375 new_coord = wp->coord;
9378 marker_end_move ( t );
9380 vtl->current_wp->coord = new_coord;
9382 trw_layer_calculate_bounds_waypoints ( vtl );
9383 vik_layer_emit_update ( VIK_LAYER(vtl) );
9386 /* PUT IN RIGHT PLACE!!! */
9387 if ( event->button == 3 && vtl->waypoint_rightclick )
9389 if ( vtl->wp_right_click_menu )
9390 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9391 if ( vtl->current_wp ) {
9392 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9393 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 );
9394 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9396 vtl->waypoint_rightclick = FALSE;
9401 /*** New track ****/
9403 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9410 GdkDrawable *drawable;
9416 * Draw specified pixmap
9418 static gboolean draw_sync ( gpointer data )
9420 draw_sync_t *ds = (draw_sync_t*) data;
9421 // Sometimes don't want to draw
9422 // normally because another update has taken precedent such as panning the display
9423 // which means this pixmap is no longer valid
9424 if ( ds->vtl->draw_sync_do ) {
9425 gdk_threads_enter();
9426 gdk_draw_drawable (ds->drawable,
9429 0, 0, 0, 0, -1, -1);
9430 ds->vtl->draw_sync_done = TRUE;
9431 gdk_threads_leave();
9437 static gchar* distance_string (gdouble distance)
9441 /* draw label with distance */
9442 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9443 switch (dist_units) {
9444 case VIK_UNITS_DISTANCE_MILES:
9445 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9446 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9447 } else if (distance < 1609.4) {
9448 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9450 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9453 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9454 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9455 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9456 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9457 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9459 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9463 // VIK_UNITS_DISTANCE_KILOMETRES
9464 if (distance >= 1000 && distance < 100000) {
9465 g_sprintf(str, "%3.2f km", distance/1000.0);
9466 } else if (distance < 1000) {
9467 g_sprintf(str, "%d m", (int)distance);
9469 g_sprintf(str, "%d km", (int)distance/1000);
9473 return g_strdup (str);
9477 * Actually set the message in statusbar
9479 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9481 // Only show elevation data when track has some elevation properties
9482 gchar str_gain_loss[64];
9483 str_gain_loss[0] = '\0';
9484 gchar str_last_step[64];
9485 str_last_step[0] = '\0';
9486 gchar *str_total = distance_string (distance);
9488 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9489 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9490 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9492 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9495 if ( last_step > 0 ) {
9496 gchar *tmp = distance_string (last_step);
9497 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9501 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9503 // Write with full gain/loss information
9504 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9505 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9507 g_free ( str_total );
9511 * Figure out what information should be set in the statusbar and then write it
9513 static void update_statusbar ( VikTrwLayer *vtl )
9515 // Get elevation data
9516 gdouble elev_gain, elev_loss;
9517 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9519 /* Find out actual distance of current track */
9520 gdouble distance = vik_track_get_length (vtl->current_track);
9522 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9526 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9528 /* if we haven't sync'ed yet, we don't have time to do more. */
9529 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9530 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9532 static GdkPixmap *pixmap = NULL;
9534 // Need to check in case window has been resized
9535 w1 = vik_viewport_get_width(vvp);
9536 h1 = vik_viewport_get_height(vvp);
9538 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9540 gdk_drawable_get_size (pixmap, &w2, &h2);
9541 if (w1 != w2 || h1 != h2) {
9542 g_object_unref ( G_OBJECT ( pixmap ) );
9543 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9546 // Reset to background
9547 gdk_draw_drawable (pixmap,
9548 vtl->current_track_newpoint_gc,
9549 vik_viewport_get_pixmap(vvp),
9550 0, 0, 0, 0, -1, -1);
9552 draw_sync_t *passalong;
9555 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9557 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9558 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9559 // thus when we come to reset to the background it would include what we have already drawn!!
9560 gdk_draw_line ( pixmap,
9561 vtl->current_track_newpoint_gc,
9562 x1, y1, event->x, event->y );
9563 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9565 /* Find out actual distance of current track */
9566 gdouble distance = vik_track_get_length (vtl->current_track);
9568 // Now add distance to where the pointer is //
9571 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9572 vik_coord_to_latlon ( &coord, &ll );
9573 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9574 distance = distance + last_step;
9576 // Get elevation data
9577 gdouble elev_gain, elev_loss;
9578 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9580 // Adjust elevation data (if available) for the current pointer position
9582 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9583 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9584 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9585 // Adjust elevation of last track point
9586 if ( elev_new > last_tpt->altitude )
9588 elev_gain += elev_new - last_tpt->altitude;
9591 elev_loss += last_tpt->altitude - elev_new;
9596 // Display of the distance 'tooltip' during track creation is controlled by a preference
9598 if ( a_vik_get_create_track_tooltip() ) {
9600 gchar *str = distance_string (distance);
9602 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9603 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9604 pango_layout_set_text (pl, str, -1);
9606 pango_layout_get_pixel_size ( pl, &wd, &hd );
9609 // offset from cursor a bit depending on font size
9613 // Create a background block to make the text easier to read over the background map
9614 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9615 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9616 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9618 g_object_unref ( G_OBJECT ( pl ) );
9619 g_object_unref ( G_OBJECT ( background_block_gc ) );
9623 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9624 passalong->vtl = vtl;
9625 passalong->pixmap = pixmap;
9626 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9627 passalong->gc = vtl->current_track_newpoint_gc;
9631 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9633 // Update statusbar with full gain/loss information
9634 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9636 // draw pixmap when we have time to
9637 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9638 vtl->draw_sync_done = FALSE;
9639 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9641 return VIK_LAYER_TOOL_ACK;
9644 // NB vtl->current_track must be valid
9645 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9648 if ( vtl->current_track->trackpoints ) {
9649 // TODO rework this...
9650 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9651 GList *last = g_list_last(vtl->current_track->trackpoints);
9652 g_free ( last->data );
9653 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9655 vik_track_calculate_bounds ( vtl->current_track );
9659 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9661 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9662 // Bin track if only one point as it's not very useful
9663 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9664 if ( vtl->current_track->is_route )
9665 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9667 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9669 vtl->current_track = NULL;
9670 vik_layer_emit_update ( VIK_LAYER(vtl) );
9672 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9673 undo_trackpoint_add ( vtl );
9674 update_statusbar ( vtl );
9675 vik_layer_emit_update ( VIK_LAYER(vtl) );
9682 * Common function to handle trackpoint button requests on either a route or a track
9683 * . enables adding a point via normal click
9684 * . enables removal of last point via right click
9685 * . finishing of the track or route via double clicking
9687 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9691 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9694 if ( event->button == 2 ) {
9695 // As the display is panning, the new track pixmap is now invalid so don't draw it
9696 // otherwise this drawing done results in flickering back to an old image
9697 vtl->draw_sync_do = FALSE;
9701 if ( event->button == 3 )
9703 if ( !vtl->current_track )
9705 undo_trackpoint_add ( vtl );
9706 update_statusbar ( vtl );
9707 vik_layer_emit_update ( VIK_LAYER(vtl) );
9711 if ( event->type == GDK_2BUTTON_PRESS )
9713 /* subtract last (duplicate from double click) tp then end */
9714 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9716 /* undo last, then end */
9717 undo_trackpoint_add ( vtl );
9718 vtl->current_track = NULL;
9720 vik_layer_emit_update ( VIK_LAYER(vtl) );
9724 tp = vik_trackpoint_new();
9725 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9727 /* snap to other TP */
9728 if ( event->state & GDK_CONTROL_MASK )
9730 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9732 tp->coord = other_tp->coord;
9735 tp->newsegment = FALSE;
9736 tp->has_timestamp = FALSE;
9739 if ( vtl->current_track ) {
9740 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9741 /* Auto attempt to get elevation from DEM data (if it's available) */
9742 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9745 vtl->ct_x1 = vtl->ct_x2;
9746 vtl->ct_y1 = vtl->ct_y2;
9747 vtl->ct_x2 = event->x;
9748 vtl->ct_y2 = event->y;
9750 vik_layer_emit_update ( VIK_LAYER(vtl) );
9754 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9756 // if we were running the route finder, cancel it
9757 vtl->route_finder_started = FALSE;
9759 // ----------------------------------------------------- if current is a route - switch to new track
9760 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9762 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9763 if ( a_vik_get_ask_for_create_track_name() ) {
9764 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
9768 new_track_create_common ( vtl, name );
9771 return tool_new_track_or_route_click ( vtl, event, vvp );
9774 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9776 if ( event->button == 2 ) {
9777 // Pan moving ended - enable potential point drawing again
9778 vtl->draw_sync_do = TRUE;
9779 vtl->draw_sync_done = TRUE;
9783 /*** New route ****/
9785 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9790 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9792 // if we were running the route finder, cancel it
9793 vtl->route_finder_started = FALSE;
9795 // -------------------------- if current is a track - switch to new route,
9796 if ( event->button == 1 && ( ! vtl->current_track ||
9797 (vtl->current_track && !vtl->current_track->is_route ) ) )
9799 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9800 if ( a_vik_get_ask_for_create_track_name() ) {
9801 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
9805 new_route_create_common ( vtl, name );
9808 return tool_new_track_or_route_click ( vtl, event, vvp );
9811 /*** New waypoint ****/
9813 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9818 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9821 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9823 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9824 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9825 trw_layer_calculate_bounds_waypoints ( vtl );
9826 vik_layer_emit_update ( VIK_LAYER(vtl) );
9832 /*** Edit trackpoint ****/
9834 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9836 tool_ed_t *t = g_new(tool_ed_t, 1);
9842 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9847 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9849 tool_ed_t *t = data;
9850 VikViewport *vvp = t->vvp;
9851 TPSearchParams params;
9852 /* OUTDATED DOCUMENTATION:
9853 find 5 pixel range on each side. then put these UTM, and a pointer
9854 to the winning track name (and maybe the winning track itself), and a
9855 pointer to the winning trackpoint, inside an array or struct. pass
9856 this along, do a foreach on the tracks which will do a foreach on the
9859 params.x = event->x;
9860 params.y = event->y;
9861 params.closest_track_id = NULL;
9862 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9863 params.closest_tp = NULL;
9864 params.closest_tpl = NULL;
9865 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9867 if ( event->button != 1 )
9870 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9873 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9876 if ( vtl->current_tpl )
9878 /* 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.) */
9879 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9880 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9885 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9887 if ( current_tr->visible &&
9888 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9889 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9890 marker_begin_move ( t, event->x, event->y );
9896 if ( vtl->tracks_visible )
9897 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9899 if ( params.closest_tp )
9901 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9902 vtl->current_tpl = params.closest_tpl;
9903 vtl->current_tp_id = params.closest_track_id;
9904 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9905 trw_layer_tpwin_init ( vtl );
9906 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9907 vik_layer_emit_update ( VIK_LAYER(vtl) );
9911 if ( vtl->routes_visible )
9912 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9914 if ( params.closest_tp )
9916 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9917 vtl->current_tpl = params.closest_tpl;
9918 vtl->current_tp_id = params.closest_track_id;
9919 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9920 trw_layer_tpwin_init ( vtl );
9921 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9922 vik_layer_emit_update ( VIK_LAYER(vtl) );
9926 /* these aren't the droids you're looking for */
9930 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9932 tool_ed_t *t = data;
9933 VikViewport *vvp = t->vvp;
9935 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9941 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9944 if ( event->state & GDK_CONTROL_MASK )
9946 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9947 if ( tp && tp != vtl->current_tpl->data )
9948 new_coord = tp->coord;
9950 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9953 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9954 marker_moveto ( t, x, y );
9962 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9964 tool_ed_t *t = data;
9965 VikViewport *vvp = t->vvp;
9967 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9969 if ( event->button != 1)
9974 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9977 if ( event->state & GDK_CONTROL_MASK )
9979 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9980 if ( tp && tp != vtl->current_tpl->data )
9981 new_coord = tp->coord;
9984 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9985 if ( vtl->current_tp_track )
9986 vik_track_calculate_bounds ( vtl->current_tp_track );
9988 marker_end_move ( t );
9990 /* diff dist is diff from orig */
9992 my_tpwin_set_tp ( vtl );
9994 vik_layer_emit_update ( VIK_LAYER(vtl) );
10001 /*** Extended Route Finder ***/
10003 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10008 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10011 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10013 g_free ( new_end );
10014 vik_layer_emit_update ( VIK_LAYER(vtl) );
10016 /* remove last ' to:...' */
10017 if ( vtl->current_track->comment ) {
10018 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10019 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10020 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10021 last_to - vtl->current_track->comment - 1);
10022 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10029 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10032 if ( !vtl ) return FALSE;
10033 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10034 if ( event->button == 3 && vtl->current_track ) {
10035 tool_extended_route_finder_undo ( vtl );
10037 else if ( event->button == 2 ) {
10038 vtl->draw_sync_do = FALSE;
10041 // if we started the track but via undo deleted all the track points, begin again
10042 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10043 return tool_new_track_or_route_click ( vtl, event, vvp );
10045 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10046 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10047 struct LatLon start, end;
10049 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10050 vik_coord_to_latlon ( &(tp_start->coord), &start );
10051 vik_coord_to_latlon ( &(tmp), &end );
10053 vtl->route_finder_started = TRUE;
10054 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10056 // update UI to let user know what's going on
10057 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10058 VikRoutingEngine *engine = vik_routing_default_engine ( );
10060 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10063 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10064 vik_routing_engine_get_label ( engine ),
10065 start.lat, start.lon, end.lat, end.lon );
10066 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10068 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10071 /* Give GTK a change to display the new status bar before querying the web */
10072 while ( gtk_events_pending ( ) )
10073 gtk_main_iteration ( );
10075 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10077 /* Update UI to say we're done */
10078 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10079 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10080 vik_routing_engine_get_label ( engine ),
10081 start.lat, start.lon, end.lat, end.lon )
10082 : g_strdup_printf ( _("Error getting route from %s."),
10083 vik_routing_engine_get_label ( engine ) );
10084 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10087 vik_layer_emit_update ( VIK_LAYER(vtl) );
10089 vtl->current_track = NULL;
10091 // create a new route where we will add the planned route to
10092 gboolean ret = tool_new_route_click( vtl, event, vvp );
10094 vtl->route_finder_started = TRUE;
10101 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10103 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10104 vtl->route_finder_started = FALSE;
10105 vtl->current_track = NULL;
10106 vik_layer_emit_update ( VIK_LAYER(vtl) );
10108 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10109 tool_extended_route_finder_undo ( vtl );
10116 /*** Show picture ****/
10118 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10123 /* Params are: vvp, event, last match found or NULL */
10124 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10126 if ( wp->image && wp->visible )
10128 gint x, y, slackx, slacky;
10129 GdkEventButton *event = (GdkEventButton *) params[1];
10131 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10132 slackx = wp->image_width / 2;
10133 slacky = wp->image_height / 2;
10134 if ( x <= event->x + slackx && x >= event->x - slackx
10135 && y <= event->y + slacky && y >= event->y - slacky )
10137 params[2] = wp->image; /* we've found a match. however continue searching
10138 * since we want to find the last match -- that
10139 * is, the match that was drawn last. */
10144 static void trw_layer_show_picture ( menu_array_sublayer values )
10146 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10148 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10149 #else /* WINDOWS */
10150 GError *err = NULL;
10151 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10152 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10153 g_free ( quoted_file );
10154 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10156 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() );
10157 g_error_free ( err );
10160 #endif /* WINDOWS */
10163 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10165 gpointer params[3] = { vvp, event, NULL };
10166 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10168 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10171 static menu_array_sublayer values;
10172 values[MA_VTL] = vtl;
10173 values[MA_MISC] = params[2];
10174 trw_layer_show_picture ( values );
10175 return TRUE; /* found a match */
10178 return FALSE; /* go through other layers, searching for a match */
10181 /***************************************************************************
10183 ***************************************************************************/
10186 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10188 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10189 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10192 /* Structure for thumbnail creating data used in the background thread */
10194 VikTrwLayer *vtl; // Layer needed for redrawing
10195 GSList *pics; // Image list
10196 } thumbnail_create_thread_data;
10198 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10200 guint total = g_slist_length(tctd->pics), done = 0;
10201 while ( tctd->pics )
10203 a_thumbnails_create ( (gchar *) tctd->pics->data );
10204 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10206 return -1; /* Abort thread */
10208 tctd->pics = tctd->pics->next;
10211 // Redraw to show the thumbnails as they are now created
10212 if ( IS_VIK_LAYER(tctd->vtl) )
10213 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10218 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10220 while ( tctd->pics )
10222 g_free ( tctd->pics->data );
10223 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10228 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10230 if ( ! vtl->has_verified_thumbnails )
10232 GSList *pics = NULL;
10233 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10236 gint len = g_slist_length ( pics );
10237 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10238 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10241 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10243 (vik_thr_func) create_thumbnails_thread,
10245 (vik_thr_free_func) thumbnail_create_thread_free,
10253 static const gchar* my_track_colors ( gint ii )
10255 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10267 // Fast and reliable way of returning a colour
10268 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10271 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10273 GHashTableIter iter;
10274 gpointer key, value;
10278 g_hash_table_iter_init ( &iter, vtl->tracks );
10280 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10282 // Tracks get a random spread of colours if not already assigned
10283 if ( ! VIK_TRACK(value)->has_color ) {
10284 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10285 VIK_TRACK(value)->color = vtl->track_color;
10287 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10289 VIK_TRACK(value)->has_color = TRUE;
10292 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10295 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10301 g_hash_table_iter_init ( &iter, vtl->routes );
10303 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10305 // Routes get an intermix of reds
10306 if ( ! VIK_TRACK(value)->has_color ) {
10308 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10310 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10311 VIK_TRACK(value)->has_color = TRUE;
10314 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10321 * (Re)Calculate the bounds of the waypoints in this layer,
10322 * This should be called whenever waypoints are changed
10324 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10326 struct LatLon topleft = { 0.0, 0.0 };
10327 struct LatLon bottomright = { 0.0, 0.0 };
10330 GHashTableIter iter;
10331 gpointer key, value;
10333 g_hash_table_iter_init ( &iter, vtl->waypoints );
10335 // Set bounds to first point
10336 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10337 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10338 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10341 // Ensure there is another point...
10342 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10344 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10346 // See if this point increases the bounds.
10347 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10349 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10350 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10351 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10352 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10356 vtl->waypoints_bbox.north = topleft.lat;
10357 vtl->waypoints_bbox.east = bottomright.lon;
10358 vtl->waypoints_bbox.south = bottomright.lat;
10359 vtl->waypoints_bbox.west = topleft.lon;
10362 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10364 vik_track_calculate_bounds ( trk );
10367 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10369 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10370 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10373 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10375 if ( ! VIK_LAYER(vtl)->vt )
10378 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10379 if ( g_hash_table_size (vtl->tracks) > 1 )
10380 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10382 if ( g_hash_table_size (vtl->routes) > 1 )
10383 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10385 if ( g_hash_table_size (vtl->waypoints) > 1 )
10386 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10389 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10391 if ( VIK_LAYER(vtl)->realized )
10392 trw_layer_verify_thumbnails ( vtl, vvp );
10393 trw_layer_track_alloc_colors ( vtl );
10395 trw_layer_calculate_bounds_waypoints ( vtl );
10396 trw_layer_calculate_bounds_tracks ( vtl );
10398 // Apply treeview sort after loading all the tracks for this layer
10399 // (rather than sorted insert on each individual track additional)
10400 // and after subsequent changes to the properties as the specified order may have changed.
10401 // since the sorting of a treeview section is now very quick
10402 // NB sorting is also performed after every name change as well to maintain the list order
10403 trw_layer_sort_all ( vtl );
10405 // Setting metadata time if not otherwise set
10406 if ( vtl->metadata ) {
10408 gboolean need_to_set_time = TRUE;
10409 if ( vtl->metadata->timestamp ) {
10410 need_to_set_time = FALSE;
10411 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10412 need_to_set_time = TRUE;
10415 if ( need_to_set_time ) {
10416 // Could rewrite this as a general get first time of a TRW Layer function
10417 GTimeVal timestamp;
10418 timestamp.tv_usec = 0;
10419 gboolean has_timestamp = FALSE;
10422 gl = g_hash_table_get_values ( vtl->tracks );
10423 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10424 gl = g_list_first ( gl );
10426 // Check times of tracks
10428 // Only need to check the first track as they have been sorted by time
10429 VikTrack *trk = (VikTrack*)gl->data;
10430 // Assume trackpoints already sorted by time
10431 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10432 if ( tpt && tpt->has_timestamp ) {
10433 timestamp.tv_sec = tpt->timestamp;
10434 has_timestamp = TRUE;
10436 g_list_free ( gl );
10439 if ( !has_timestamp ) {
10440 // 'Last' resort - current time
10441 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10442 g_get_current_time ( ×tamp );
10444 // Check times of waypoints
10445 gl = g_hash_table_get_values ( vtl->waypoints );
10447 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10448 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10449 if ( wpt->has_timestamp ) {
10450 if ( timestamp.tv_sec > wpt->timestamp ) {
10451 timestamp.tv_sec = wpt->timestamp;
10452 has_timestamp = TRUE;
10456 g_list_free ( gl );
10459 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10464 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10466 return vtl->coord_mode;
10470 * Uniquify the whole layer
10471 * Also requires the layers panel as the names shown there need updating too
10472 * Returns whether the operation was successful or not
10474 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10476 if ( vtl && vlp ) {
10477 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10478 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10479 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10485 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10487 vik_coord_convert ( &(wp->coord), *dest_mode );
10490 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10492 vik_track_convert ( tr, *dest_mode );
10495 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10497 if ( vtl->coord_mode != dest_mode )
10499 vtl->coord_mode = dest_mode;
10500 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10501 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10502 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10506 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10508 vtl->menu_selection = selection;
10511 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10513 return (vtl->menu_selection);
10516 /* ----------- Downloading maps along tracks --------------- */
10518 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10520 /* TODO: calculating based on current size of viewport */
10521 const gdouble w_at_zoom_0_125 = 0.0013;
10522 const gdouble h_at_zoom_0_125 = 0.0011;
10523 gdouble zoom_factor = zoom_level/0.125;
10525 wh->lat = h_at_zoom_0_125 * zoom_factor;
10526 wh->lon = w_at_zoom_0_125 * zoom_factor;
10528 return 0; /* all OK */
10531 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10533 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10534 (dist->lat >= ABS(to->north_south - from->north_south)))
10537 VikCoord *coord = g_malloc(sizeof(VikCoord));
10538 coord->mode = VIK_COORD_LATLON;
10540 if (ABS(gradient) < 1) {
10541 if (from->east_west > to->east_west)
10542 coord->east_west = from->east_west - dist->lon;
10544 coord->east_west = from->east_west + dist->lon;
10545 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10547 if (from->north_south > to->north_south)
10548 coord->north_south = from->north_south - dist->lat;
10550 coord->north_south = from->north_south + dist->lat;
10551 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10557 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10559 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10560 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10562 VikCoord *next = from;
10564 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10566 list = g_list_prepend(list, next);
10572 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10574 typedef struct _Rect {
10579 #define GLRECT(iter) ((Rect *)((iter)->data))
10582 GList *rects_to_download = NULL;
10585 if (get_download_area_width(vvp, zoom_level, &wh))
10588 GList *iter = tr->trackpoints;
10592 gboolean new_map = TRUE;
10593 VikCoord *cur_coord, tl, br;
10596 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10598 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10599 rect = g_malloc(sizeof(Rect));
10602 rect->center = *cur_coord;
10603 rects_to_download = g_list_prepend(rects_to_download, rect);
10608 gboolean found = FALSE;
10609 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10610 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10621 GList *fillins = NULL;
10622 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10623 /* seems that ATM the function get_next_coord works only for LATLON */
10624 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10625 /* fill-ins for far apart points */
10626 GList *cur_rect, *next_rect;
10627 for (cur_rect = rects_to_download;
10628 (next_rect = cur_rect->next) != NULL;
10629 cur_rect = cur_rect->next) {
10630 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10631 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10632 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10636 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10639 GList *fiter = fillins;
10641 cur_coord = (VikCoord *)(fiter->data);
10642 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10643 rect = g_malloc(sizeof(Rect));
10646 rect->center = *cur_coord;
10647 rects_to_download = g_list_prepend(rects_to_download, rect);
10648 fiter = fiter->next;
10652 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10653 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10657 for (iter = fillins; iter; iter = iter->next)
10658 g_free(iter->data);
10659 g_list_free(fillins);
10661 if (rects_to_download) {
10662 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10663 g_free(rect_iter->data);
10664 g_list_free(rects_to_download);
10668 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10672 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10673 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10674 gint selected_zoom, default_zoom;
10676 VikTrwLayer *vtl = values[MA_VTL];
10677 VikLayersPanel *vlp = values[MA_VLP];
10679 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10680 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10682 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10686 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10688 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10689 int num_maps = g_list_length(vmls);
10692 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10696 // Convert from list of vmls to list of names. Allowing the user to select one of them
10697 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10698 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10700 gchar **np = map_names;
10701 VikMapsLayer **lp = map_layers;
10703 for (i = 0; i < num_maps; i++) {
10704 vml = (VikMapsLayer *)(vmls->data);
10706 *np++ = vik_maps_layer_get_map_label(vml);
10709 // Mark end of the array lists
10713 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10714 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10715 if (cur_zoom == zoom_vals[default_zoom])
10718 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10720 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10723 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10726 for (i = 0; i < num_maps; i++)
10727 g_free(map_names[i]);
10729 g_free(map_layers);
10735 /**** lowest waypoint number calculation ***/
10736 static gint highest_wp_number_name_to_number(const gchar *name) {
10737 if ( strlen(name) == 3 ) {
10738 int n = atoi(name);
10739 if ( n < 100 && name[0] != '0' )
10741 if ( n < 10 && name[0] != '0' )
10749 static void highest_wp_number_reset(VikTrwLayer *vtl)
10751 vtl->highest_wp_number = -1;
10754 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10756 /* if is bigger that top, add it */
10757 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10758 if ( new_wp_num > vtl->highest_wp_number )
10759 vtl->highest_wp_number = new_wp_num;
10762 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10764 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10765 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10766 if ( vtl->highest_wp_number == old_wp_num ) {
10768 vtl->highest_wp_number--;
10770 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10771 /* search down until we find something that *does* exist */
10773 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10774 vtl->highest_wp_number--;
10775 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10780 /* get lowest unused number */
10781 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10784 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10786 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10787 return g_strdup(buf);
10791 * trw_layer_create_track_list_both:
10793 * Create the latest list of tracks and routes
10795 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10797 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10798 GList *tracks = NULL;
10799 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10800 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10802 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10805 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10807 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10809 gchar *title = NULL;
10810 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10811 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10813 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10815 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10819 static void trw_layer_track_list_dialog ( menu_array_layer values )
10821 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10823 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10824 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10828 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10830 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10832 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10833 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );