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, GdkEventButton *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 *stdout = NULL;
788 gchar *stderr = NULL;
789 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
790 if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
791 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
793 g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
795 g_warning ("Diary: stderr: %s", stderr );
797 gchar **tokens = NULL;
798 if ( stdout && g_strcmp0(stdout, "") )
799 tokens = g_strsplit(stdout, " ", 0);
801 tokens = g_strsplit(stderr, " ", 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 // VIK_UNITS_DISTANCE_KILOMETRES:
1745 mydist = dist/1000.0;
1752 * trw_layer_draw_dist_labels:
1754 * Draw a few labels along a track at nicely seperated distances
1755 * This might slow things down if there's many tracks being displayed with this on.
1757 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1759 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1760 25.0, 40.0, 50.0, 75.0, 100.0,
1761 150.0, 200.0, 250.0, 500.0, 1000.0};
1763 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1765 // Convert to specified unit to find the friendly breakdown value
1766 dist = distance_in_preferred_units ( dist );
1770 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1771 if ( chunksd[i] > dist ) {
1773 dist = chunksd[index];
1778 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1780 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1781 gdouble dist_i = dist * i;
1783 // Convert distance back into metres for use in finding a trackpoint
1784 switch (dist_units) {
1785 case VIK_UNITS_DISTANCE_MILES:
1786 dist_i = VIK_MILES_TO_METERS(dist_i);
1788 // VIK_UNITS_DISTANCE_KILOMETRES:
1790 dist_i = dist_i*1000.0;
1794 gdouble dist_current = 0.0;
1795 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1796 gdouble dist_next = 0.0;
1797 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1799 gdouble dist_between_tps = fabs (dist_next - dist_current);
1800 gdouble ratio = 0.0;
1801 // Prevent division by 0 errors
1802 if ( dist_between_tps > 0.0 )
1803 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1805 if ( tp_current && tp_next ) {
1806 // Construct the name based on the distance value
1809 switch (dist_units) {
1810 case VIK_UNITS_DISTANCE_MILES:
1811 units = g_strdup ( _("miles") );
1813 // VIK_UNITS_DISTANCE_KILOMETRES:
1815 units = g_strdup ( _("km") );
1819 // Convert for display
1820 dist_i = distance_in_preferred_units ( dist_i );
1822 // Make the precision of the output related to the unit size.
1824 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1825 else if ( index == 1 )
1826 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1828 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1831 struct LatLon ll_current, ll_next;
1832 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1833 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1835 // positional interpolation
1836 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1837 // but should be good enough over the small scale that I anticipate usage on
1838 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1839 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1841 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1844 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1845 fgcolour = gdk_color_to_string ( &(trk->color) );
1847 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1849 // if highlight mode on, then colour the background in the highlight colour
1851 if ( drawing_highlight )
1852 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1854 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1856 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1858 g_free ( fgcolour );
1859 g_free ( bgcolour );
1866 * trw_layer_draw_track_name_labels:
1868 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1870 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1873 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1874 fgcolour = gdk_color_to_string ( &(trk->color) );
1876 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1878 // if highlight mode on, then colour the background in the highlight colour
1880 if ( drawing_highlight )
1881 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1883 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1885 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1887 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1888 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1889 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1890 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1891 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1892 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1894 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1896 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1899 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1900 // No other labels to draw
1903 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1906 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1909 VikCoord begin_coord = tp_begin->coord;
1910 VikCoord end_coord = tp_end->coord;
1912 gboolean done_start_end = FALSE;
1914 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1915 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1917 // This number can be configured via the settings if you really want to change it
1918 gdouble distance_diff;
1919 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1920 distance_diff = 100.0; // Metres
1922 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1923 // Start and end 'close' together so only draw one label at an average location
1924 gint x1, x2, y1, y2;
1925 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1926 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1928 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1930 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1931 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1934 done_start_end = TRUE;
1938 if ( ! done_start_end ) {
1939 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1940 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1941 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1942 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1943 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1944 g_free ( name_start );
1946 // Don't draw end label if this is the one being created
1947 if ( trk != dp->vtl->current_track ) {
1948 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1949 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1950 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1951 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1952 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1953 g_free ( name_end );
1958 g_free ( fgcolour );
1959 g_free ( bgcolour );
1963 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1965 if ( ! track->visible )
1968 /* TODO: this function is a mess, get rid of any redundancy */
1969 GList *list = track->trackpoints;
1971 gboolean useoldvals = TRUE;
1973 gboolean drawpoints;
1975 gboolean drawelevation;
1976 gdouble min_alt, max_alt, alt_diff = 0;
1978 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1979 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1982 if ( dp->vtl->drawelevation )
1984 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1985 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1986 alt_diff = max_alt - min_alt;
1989 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1990 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1991 trw_layer_draw_track ( id, track, dp, TRUE );
1993 if ( draw_track_outline )
1994 drawpoints = drawstops = FALSE;
1996 drawpoints = dp->vtl->drawpoints;
1997 drawstops = dp->vtl->drawstops;
2000 gboolean drawing_highlight = FALSE;
2001 /* Current track - used for creation */
2002 if ( track == dp->vtl->current_track )
2003 main_gc = dp->vtl->current_track_gc;
2005 if ( dp->highlight ) {
2006 /* Draw all tracks of the layer in special colour
2007 NB this supercedes the drawmode */
2008 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2009 drawing_highlight = TRUE;
2011 if ( !drawing_highlight ) {
2012 // Still need to figure out the gc according to the drawing mode:
2013 switch ( dp->vtl->drawmode ) {
2014 case DRAWMODE_BY_TRACK:
2015 if ( dp->vtl->track_1color_gc )
2016 g_object_unref ( dp->vtl->track_1color_gc );
2017 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2018 main_gc = dp->vtl->track_1color_gc;
2021 // Mostly for DRAWMODE_ALL_SAME_COLOR
2022 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2023 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2030 int x, y, oldx, oldy;
2031 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2033 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2035 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2037 // Draw the first point as something a bit different from the normal points
2038 // ATM it's slightly bigger and a triangle
2040 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2041 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2047 gdouble average_speed = 0.0;
2048 gdouble low_speed = 0.0;
2049 gdouble high_speed = 0.0;
2050 // If necessary calculate these values - which is done only once per track redraw
2051 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2052 // the percentage factor away from the average speed determines transistions between the levels
2053 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2054 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2055 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2058 while ((list = g_list_next(list)))
2060 tp = VIK_TRACKPOINT(list->data);
2061 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2063 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2064 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2065 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2066 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2067 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2069 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2072 * If points are the same in display coordinates, don't draw.
2074 if ( useoldvals && x == oldx && y == oldy )
2076 // Still need to process points to ensure 'stops' are drawn if required
2077 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2078 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2079 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 );
2084 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2085 if ( drawpoints || dp->vtl->drawlines ) {
2086 // setup main_gc for both point and line drawing
2087 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2088 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 ) );
2092 if ( drawpoints && ! draw_track_outline )
2097 * The concept of drawing stops is that a trackpoint
2098 * that is if the next trackpoint has a timestamp far into
2099 * the future, we draw a circle of 6x trackpoint size,
2100 * instead of a rectangle of 2x trackpoint size.
2101 * This is drawn first so the trackpoint will be drawn on top
2104 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2105 /* Stop point. Draw 6x circle. Always in redish colour */
2106 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 );
2108 /* Regular point - draw 2x square. */
2109 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2112 /* Final point - draw 4x circle. */
2113 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 );
2116 if ((!tp->newsegment) && (dp->vtl->drawlines))
2119 /* UTM only: zone check */
2120 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2121 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2124 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2126 if ( draw_track_outline ) {
2127 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2131 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2133 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2135 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2140 tmp[1].y = oldy-FIXALTITUDE(list->data);
2142 tmp[2].y = y-FIXALTITUDE(list->next->data);
2147 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2148 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2150 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2151 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2153 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2158 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2159 // Draw an arrow at the mid point to show the direction of the track
2160 // Code is a rework from vikwindow::draw_ruler()
2161 gint midx = (oldx + x) / 2;
2162 gint midy = (oldy + y) / 2;
2164 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2165 // Avoid divide by zero and ensure at least 1 pixel big
2167 gdouble dx = (oldx - midx) / len;
2168 gdouble dy = (oldy - midy) / len;
2169 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2170 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2180 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2182 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2183 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2185 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2187 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2188 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 ));
2192 * If points are the same in display coordinates, don't draw.
2194 if ( x != oldx || y != oldy )
2196 if ( draw_track_outline )
2197 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2199 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2205 * If points are the same in display coordinates, don't draw.
2207 if ( x != oldx && y != oldy )
2209 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2210 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2218 // Labels drawn after the trackpoints, so the labels are on top
2219 if ( dp->vtl->track_draw_labels ) {
2220 if ( track->max_number_dist_labels > 0 ) {
2221 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2224 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2225 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2231 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2233 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2234 trw_layer_draw_track ( id, track, dp, FALSE );
2238 static void cached_pixbuf_free ( CachedPixbuf *cp )
2240 g_object_unref ( G_OBJECT(cp->pixbuf) );
2241 g_free ( cp->image );
2244 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2246 return strcmp ( cp->image, name );
2249 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2252 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2253 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2254 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2257 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2259 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2261 if ( wp->image && dp->vtl->drawimages )
2263 GdkPixbuf *pixbuf = NULL;
2266 if ( dp->vtl->image_alpha == 0)
2269 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2271 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2274 gchar *image = wp->image;
2275 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2276 if ( ! regularthumb )
2278 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2279 image = "\x12\x00"; /* this shouldn't occur naturally. */
2283 CachedPixbuf *cp = NULL;
2284 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2285 if ( dp->vtl->image_size == 128 )
2286 cp->pixbuf = regularthumb;
2289 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2290 g_assert ( cp->pixbuf );
2291 g_object_unref ( G_OBJECT(regularthumb) );
2293 cp->image = g_strdup ( image );
2295 /* needed so 'click picture' tool knows how big the pic is; we don't
2296 * store it in cp because they may have been freed already. */
2297 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2298 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2300 g_queue_push_head ( dp->vtl->image_cache, cp );
2301 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2302 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2304 pixbuf = cp->pixbuf;
2308 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2314 w = gdk_pixbuf_get_width ( pixbuf );
2315 h = gdk_pixbuf_get_height ( pixbuf );
2317 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2319 if ( dp->highlight ) {
2320 // Highlighted - so draw a little border around the chosen one
2321 // single line seems a little weak so draw 2 of them
2322 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2323 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2324 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2325 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2328 if ( dp->vtl->image_alpha == 255 )
2329 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2331 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2333 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2337 // Draw appropriate symbol - either symbol image or simple types
2338 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2339 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 );
2341 else if ( wp == dp->vtl->current_wp ) {
2342 switch ( dp->vtl->wp_symbol ) {
2343 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;
2344 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;
2345 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;
2346 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 );
2347 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 );
2352 switch ( dp->vtl->wp_symbol ) {
2353 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;
2354 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;
2355 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;
2356 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 );
2357 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;
2362 if ( dp->vtl->drawlabels )
2364 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2365 gint label_x, label_y;
2367 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2369 // Could this stored in the waypoint rather than recreating each pass?
2370 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2372 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2373 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2375 // Fallback if parse failure
2376 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2378 g_free ( wp_label_markup );
2380 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2381 label_x = x - width/2;
2382 if ( wp->symbol_pixbuf )
2383 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2385 label_y = y - dp->vtl->wp_size - height - 2;
2387 /* if highlight mode on, then draw background text in highlight colour */
2388 if ( dp->highlight )
2389 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2391 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2392 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2397 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2399 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2400 trw_layer_draw_waypoint ( id, wp, dp );
2404 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2406 static struct DrawingParams dp;
2407 g_assert ( l != NULL );
2409 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2411 if ( l->tracks_visible )
2412 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2414 if ( l->routes_visible )
2415 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2417 if (l->waypoints_visible)
2418 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2421 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2423 // 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
2424 // This may seem slightly inefficient to test each time for every layer
2425 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2426 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2427 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2429 trw_layer_draw_with_highlight ( l, data, FALSE );
2432 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2434 // Check the layer for visibility (including all the parents visibilities)
2435 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2437 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2441 * vik_trw_layer_draw_highlight_item:
2443 * Only handles a single track or waypoint ATM
2444 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2446 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2448 // Check the layer for visibility (including all the parents visibilities)
2449 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2452 static struct DrawingParams dp;
2453 init_drawing_params ( &dp, vtl, vvp, TRUE );
2456 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2458 trw_layer_draw_track_cb ( NULL, trk, &dp );
2460 if ( vtl->waypoints_visible && wpt ) {
2461 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2466 * vik_trw_layer_draw_highlight_item:
2468 * Generally for drawing all tracks or routes or waypoints
2469 * trks may be actually routes
2470 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2472 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2474 // Check the layer for visibility (including all the parents visibilities)
2475 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2478 static struct DrawingParams dp;
2479 init_drawing_params ( &dp, vtl, vvp, TRUE );
2482 gboolean is_routes = (trks == vtl->routes);
2483 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2485 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2488 if ( vtl->waypoints_visible && wpts )
2489 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2492 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2495 if ( vtl->track_bg_gc )
2497 g_object_unref ( vtl->track_bg_gc );
2498 vtl->track_bg_gc = NULL;
2500 if ( vtl->track_1color_gc )
2502 g_object_unref ( vtl->track_1color_gc );
2503 vtl->track_1color_gc = NULL;
2505 if ( vtl->current_track_gc )
2507 g_object_unref ( vtl->current_track_gc );
2508 vtl->current_track_gc = NULL;
2510 if ( vtl->current_track_newpoint_gc )
2512 g_object_unref ( vtl->current_track_newpoint_gc );
2513 vtl->current_track_newpoint_gc = NULL;
2516 if ( ! vtl->track_gc )
2518 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2519 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2520 g_array_free ( vtl->track_gc, TRUE );
2521 vtl->track_gc = NULL;
2524 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2526 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2527 gint width = vtl->line_thickness;
2529 if ( vtl->track_gc )
2530 trw_layer_free_track_gcs ( vtl );
2532 if ( vtl->track_bg_gc )
2533 g_object_unref ( vtl->track_bg_gc );
2534 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2536 // Ensure new track drawing heeds line thickness setting
2537 // however always have a minium of 2, as 1 pixel is really narrow
2538 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2540 if ( vtl->current_track_gc )
2541 g_object_unref ( vtl->current_track_gc );
2542 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2543 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2545 // 'newpoint' gc is exactly the same as the current track gc
2546 if ( vtl->current_track_newpoint_gc )
2547 g_object_unref ( vtl->current_track_newpoint_gc );
2548 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2549 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2551 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2553 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2554 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2556 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2557 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2558 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2560 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2562 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2565 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2567 VikTrwLayer *rv = trw_layer_new1 ( vp );
2568 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2570 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2571 /* early exit, as the rest is GUI related */
2575 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2576 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2578 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2579 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2581 trw_layer_new_track_gcs ( rv, vp );
2583 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2584 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2585 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2586 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2588 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2590 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2595 #define SMALL_ICON_SIZE 18
2597 * Can accept a null symbol, and may return null value
2599 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2601 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2602 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2603 // So needing a small icon for the treeview may need some resizing:
2604 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2605 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2609 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2611 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2613 GdkPixbuf *pixbuf = NULL;
2615 if ( track->has_color ) {
2616 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2617 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2618 // Here is some magic found to do the conversion
2619 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2620 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2621 ((track->color.green & 0xff00) << 8) |
2622 (track->color.blue & 0xff00);
2624 gdk_pixbuf_fill ( pixbuf, pixel );
2627 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 );
2630 g_object_unref (pixbuf);
2632 *new_iter = *((GtkTreeIter *) pass_along[1]);
2633 if ( track->is_route )
2634 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2636 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2638 if ( ! track->visible )
2639 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2642 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2644 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2646 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 );
2648 *new_iter = *((GtkTreeIter *) pass_along[1]);
2649 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2651 if ( ! wp->visible )
2652 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2655 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2657 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2660 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2662 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2665 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2667 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2670 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2673 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2675 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2676 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2678 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2680 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2683 if ( g_hash_table_size (vtl->routes) > 0 ) {
2684 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2686 pass_along[0] = &(vtl->routes_iter);
2687 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2689 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2691 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2694 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2695 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2697 pass_along[0] = &(vtl->waypoints_iter);
2698 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2700 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2702 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2707 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2711 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2712 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2713 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2714 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2716 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2718 return (t->visible ^= 1);
2722 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2724 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2726 return (t->visible ^= 1);
2730 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2732 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2734 return (t->visible ^= 1);
2744 * Return a property about tracks for this layer
2746 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2748 return vtl->line_thickness;
2751 // Structure to hold multiple track information for a layer
2760 * Build up layer multiple track information via updating the tooltip_tracks structure
2762 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2764 tt->length = tt->length + vik_track_get_length (tr);
2766 // Ensure times are available
2767 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2768 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2769 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2770 if ( trkpt_last->has_timestamp ) {
2772 t1 = vik_track_get_tp_first(tr)->timestamp;
2773 t2 = trkpt_last->timestamp;
2775 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2776 // Hence initialize to the first 'proper' value
2777 if ( tt->start_time == 0 )
2778 tt->start_time = t1;
2779 if ( tt->end_time == 0 )
2782 // Update find the earliest / last times
2783 if ( t1 < tt->start_time )
2784 tt->start_time = t1;
2785 if ( t2 > tt->end_time )
2788 // Keep track of total time
2789 // there maybe gaps within a track (eg segments)
2790 // but this should be generally good enough for a simple indicator
2791 tt->duration = tt->duration + (int)(t2-t1);
2797 * Generate tooltip text for the layer.
2798 * This is relatively complicated as it considers information for
2799 * no tracks, a single track or multiple tracks
2800 * (which may or may not have timing information)
2802 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2813 static gchar tmp_buf[128];
2816 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2818 // Safety check - I think these should always be valid
2819 if ( vtl->tracks && vtl->waypoints ) {
2820 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2821 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2823 GDate* gdate_start = g_date_new ();
2824 g_date_set_time_t (gdate_start, tt.start_time);
2826 GDate* gdate_end = g_date_new ();
2827 g_date_set_time_t (gdate_end, tt.end_time);
2829 if ( g_date_compare (gdate_start, gdate_end) ) {
2830 // Dates differ so print range on separate line
2831 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2832 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2833 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2836 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2837 if ( tt.start_time != 0 )
2838 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2842 if ( tt.length > 0.0 ) {
2843 gdouble len_in_units;
2845 // Setup info dependent on distance units
2846 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2847 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2848 len_in_units = VIK_METERS_TO_MILES(tt.length);
2851 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2852 len_in_units = tt.length/1000.0;
2855 // Timing information if available
2857 if ( tt.duration > 0 ) {
2858 g_snprintf (tbuf1, sizeof(tbuf1),
2859 _(" in %d:%02d hrs:mins"),
2860 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2862 g_snprintf (tbuf2, sizeof(tbuf2),
2863 _("\n%sTotal Length %.1f %s%s"),
2864 tbuf3, len_in_units, tbuf4, tbuf1);
2867 // Put together all the elements to form compact tooltip text
2868 g_snprintf (tmp_buf, sizeof(tmp_buf),
2869 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2870 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2872 g_date_free (gdate_start);
2873 g_date_free (gdate_end);
2880 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2884 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2886 // Very simple tooltip - may expand detail in the future...
2887 static gchar tmp_buf[32];
2888 g_snprintf (tmp_buf, sizeof(tmp_buf),
2890 g_hash_table_size (l->tracks));
2894 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2896 // Very simple tooltip - may expand detail in the future...
2897 static gchar tmp_buf[32];
2898 g_snprintf (tmp_buf, sizeof(tmp_buf),
2900 g_hash_table_size (l->routes));
2905 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2906 // Same tooltip for a route
2907 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2910 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2911 tr = g_hash_table_lookup ( l->tracks, sublayer );
2913 tr = g_hash_table_lookup ( l->routes, sublayer );
2916 // Could be a better way of handling strings - but this works...
2917 gchar time_buf1[20];
2918 gchar time_buf2[20];
2919 time_buf1[0] = '\0';
2920 time_buf2[0] = '\0';
2921 static gchar tmp_buf[100];
2922 // Compact info: Short date eg (11/20/99), duration and length
2923 // Hopefully these are the things that are most useful and so promoted into the tooltip
2924 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2925 // %x The preferred date representation for the current locale without the time.
2926 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2927 time_t dur = vik_track_get_duration ( tr );
2929 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2931 // Get length and consider the appropriate distance units
2932 gdouble tr_len = vik_track_get_length(tr);
2933 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2934 switch (dist_units) {
2935 case VIK_UNITS_DISTANCE_KILOMETRES:
2936 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2938 case VIK_UNITS_DISTANCE_MILES:
2939 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2948 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2950 // Very simple tooltip - may expand detail in the future...
2951 static gchar tmp_buf[32];
2952 g_snprintf (tmp_buf, sizeof(tmp_buf),
2954 g_hash_table_size (l->waypoints));
2958 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2960 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2961 // NB It's OK to return NULL
2966 return w->description;
2975 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2978 * set_statusbar_msg_info_trkpt:
2980 * Function to show track point information on the statusbar
2981 * Items displayed is controlled by the settings format code
2983 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2985 gchar *statusbar_format_code = NULL;
2986 gboolean need2free = FALSE;
2987 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2988 // Otherwise use default
2989 statusbar_format_code = g_strdup ( "KEATDN" );
2993 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2994 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2998 g_free ( statusbar_format_code );
3002 * Function to show basic waypoint information on the statusbar
3004 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3007 switch (a_vik_get_units_height ()) {
3008 case VIK_UNITS_HEIGHT_FEET:
3009 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3012 //VIK_UNITS_HEIGHT_METRES:
3013 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3017 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3018 // one can easily use the current pointer position to see this if needed
3019 gchar *lat = NULL, *lon = NULL;
3020 static struct LatLon ll;
3021 vik_coord_to_latlon (&(wpt->coord), &ll);
3022 a_coords_latlon_to_string ( &ll, &lat, &lon );
3024 // Combine parts to make overall message
3027 // Add comment if available
3028 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3030 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3031 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3038 * General layer selection function, find out which bit is selected and take appropriate action
3040 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3043 l->current_wp = NULL;
3044 l->current_wp_id = NULL;
3045 trw_layer_cancel_current_tp ( l, FALSE );
3048 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3052 case VIK_TREEVIEW_TYPE_LAYER:
3054 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3055 /* Mark for redraw */
3060 case VIK_TREEVIEW_TYPE_SUBLAYER:
3064 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3066 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3067 /* Mark for redraw */
3071 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3073 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3074 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3075 /* Mark for redraw */
3079 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3081 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3082 /* Mark for redraw */
3086 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3088 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3089 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3090 /* Mark for redraw */
3094 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3096 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3097 /* Mark for redraw */
3101 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3103 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3105 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3106 // Show some waypoint info
3107 set_statusbar_msg_info_wpt ( l, wpt );
3108 /* Mark for redraw */
3115 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3124 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3129 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3134 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3139 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3141 return l->waypoints;
3144 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3146 return vtl->tracks_iters;
3149 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3151 return vtl->routes_iters;
3154 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3156 return vtl->waypoints;
3159 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3161 return ! ( g_hash_table_size ( vtl->tracks ) ||
3162 g_hash_table_size ( vtl->routes ) ||
3163 g_hash_table_size ( vtl->waypoints ) );
3166 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3168 return vtl->tracks_visible;
3171 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3173 return vtl->routes_visible;
3176 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3178 return vtl->waypoints_visible;
3182 * ATM use a case sensitive find
3183 * Finds the first one
3185 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3187 if ( wp && wp->name )
3188 if ( ! strcmp ( wp->name, name ) )
3194 * Get waypoint by name - not guaranteed to be unique
3195 * Finds the first one
3197 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3199 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3203 * ATM use a case sensitive find
3204 * Finds the first one
3206 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3208 if ( trk && trk->name )
3209 if ( ! strcmp ( trk->name, name ) )
3215 * Get track by name - not guaranteed to be unique
3216 * Finds the first one
3218 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3220 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3224 * Get route by name - not guaranteed to be unique
3225 * Finds the first one
3227 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3229 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3232 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3234 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3235 maxmin[0].lat = trk->bbox.north;
3236 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3237 maxmin[1].lat = trk->bbox.south;
3238 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3239 maxmin[0].lon = trk->bbox.east;
3240 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3241 maxmin[1].lon = trk->bbox.west;
3244 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3246 // Continually reuse maxmin to find the latest maximum and minimum values
3247 // First set to waypoints bounds
3248 maxmin[0].lat = vtl->waypoints_bbox.north;
3249 maxmin[1].lat = vtl->waypoints_bbox.south;
3250 maxmin[0].lon = vtl->waypoints_bbox.east;
3251 maxmin[1].lon = vtl->waypoints_bbox.west;
3252 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3253 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3256 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3258 /* 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... */
3259 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3260 trw_layer_find_maxmin (vtl, maxmin);
3261 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3265 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3266 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3271 static void trw_layer_centerize ( menu_array_layer values )
3273 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3275 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3276 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3278 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3281 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3283 /* First set the center [in case previously viewing from elsewhere] */
3284 /* Then loop through zoom levels until provided positions are in view */
3285 /* This method is not particularly fast - but should work well enough */
3286 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3288 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3289 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3291 /* Convert into definite 'smallest' and 'largest' positions */
3292 struct LatLon minmin;
3293 if ( maxmin[0].lat < maxmin[1].lat )
3294 minmin.lat = maxmin[0].lat;
3296 minmin.lat = maxmin[1].lat;
3298 struct LatLon maxmax;
3299 if ( maxmin[0].lon > maxmin[1].lon )
3300 maxmax.lon = maxmin[0].lon;
3302 maxmax.lon = maxmin[1].lon;
3304 /* Never zoom in too far - generally not that useful, as too close ! */
3305 /* Always recalculate the 'best' zoom level */
3307 vik_viewport_set_zoom ( vvp, zoom );
3309 gdouble min_lat, max_lat, min_lon, max_lon;
3310 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3311 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3312 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3313 /* NB I think the logic used in this test to determine if the bounds is within view
3314 fails if track goes across 180 degrees longitude.
3315 Hopefully that situation is not too common...
3316 Mind you viking doesn't really do edge locations to well anyway */
3317 if ( min_lat < minmin.lat &&
3318 max_lat > minmin.lat &&
3319 min_lon < maxmax.lon &&
3320 max_lon > maxmax.lon )
3321 /* Found within zoom level */
3326 vik_viewport_set_zoom ( vvp, zoom );
3330 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3332 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3333 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3334 trw_layer_find_maxmin (vtl, maxmin);
3335 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3338 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3343 static void trw_layer_auto_view ( menu_array_layer values )
3345 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3346 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3347 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3348 vik_layers_panel_emit_update ( vlp );
3351 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3354 static void trw_layer_export_gpspoint ( menu_array_layer values )
3356 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3358 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3360 g_free ( auto_save_name );
3363 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3365 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3367 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3369 g_free ( auto_save_name );
3372 static void trw_layer_export_gpx ( menu_array_layer values )
3374 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3376 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3378 g_free ( auto_save_name );
3381 static void trw_layer_export_kml ( menu_array_layer values )
3383 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3385 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3387 g_free ( auto_save_name );
3390 static void trw_layer_export_geojson ( menu_array_layer values )
3392 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3394 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3396 g_free ( auto_save_name );
3399 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3401 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3402 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3405 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3407 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3410 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3412 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3415 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3417 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3419 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3420 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3422 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3424 if ( !trk || !trk->name )
3427 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3429 gchar *label = NULL;
3430 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3431 label = _("Export Route as GPX");
3433 label = _("Export Track as GPX");
3434 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3436 g_free ( auto_save_name );
3439 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3441 wpu_udata *user_data = udata;
3442 if ( wp == user_data->wp ) {
3443 user_data->uuid = id;
3449 static void trw_layer_goto_wp ( menu_array_layer values )
3451 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3452 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3453 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3454 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3455 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3457 GTK_RESPONSE_REJECT,
3459 GTK_RESPONSE_ACCEPT,
3462 GtkWidget *label, *entry;
3463 label = gtk_label_new(_("Waypoint Name:"));
3464 entry = gtk_entry_new();
3466 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3467 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3468 gtk_widget_show_all ( label );
3469 gtk_widget_show_all ( entry );
3471 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3473 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3475 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3476 // Find *first* wp with the given name
3477 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3480 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3483 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3484 vik_layers_panel_emit_update ( vlp );
3486 // Find and select on the side panel
3491 // Hmmm, want key of it
3492 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3494 if ( wpf && udata.uuid ) {
3495 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3496 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3505 gtk_widget_destroy ( dia );
3508 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3510 gchar *default_name = highest_wp_number_get(vtl);
3511 VikWaypoint *wp = vik_waypoint_new();
3512 gchar *returned_name;
3514 wp->coord = *def_coord;
3516 // Attempt to auto set height if DEM data is available
3517 vik_waypoint_apply_dem_data ( wp, TRUE );
3519 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3521 if ( returned_name )
3524 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3525 g_free (default_name);
3526 g_free (returned_name);
3529 g_free (default_name);
3530 vik_waypoint_free(wp);
3534 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3536 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3537 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3538 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3539 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3540 VikViewport *vvp = vik_window_viewport(vw);
3542 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3543 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3544 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3545 trw_layer_calculate_bounds_waypoints ( vtl );
3546 vik_layers_panel_emit_update ( vlp );
3549 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3551 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3552 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3553 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3555 trw_layer_find_maxmin (vtl, maxmin);
3556 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3557 trw_layer_calculate_bounds_waypoints ( vtl );
3558 vik_layers_panel_emit_update ( vlp );
3561 #ifdef VIK_CONFIG_GEOTAG
3562 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3564 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3566 // Update directly - not changing the mtime
3567 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3570 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3572 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3575 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3579 * Use code in separate file for this feature as reasonably complex
3581 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3583 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3584 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3585 // Unset so can be reverified later if necessary
3586 vtl->has_verified_thumbnails = FALSE;
3588 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3594 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3596 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3597 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3599 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3605 static void trw_layer_geotagging ( menu_array_layer values )
3607 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3608 // Unset so can be reverified later if necessary
3609 vtl->has_verified_thumbnails = FALSE;
3611 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3618 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3620 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3622 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3623 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3624 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3625 VikViewport *vvp = vik_window_viewport(vw);
3627 vik_datasource_mode_t mode = datasource->mode;
3628 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3629 mode = VIK_DATASOURCE_ADDTOLAYER;
3630 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3634 * Acquire into this TRW Layer straight from GPS Device
3636 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3638 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3642 * Acquire into this TRW Layer from Directions
3644 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3646 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3650 * Acquire into this TRW Layer from an entered URL
3652 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3654 trw_layer_acquire ( values, &vik_datasource_url_interface );
3657 #ifdef VIK_CONFIG_OPENSTREETMAP
3659 * Acquire into this TRW Layer from OSM
3661 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3663 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3667 * Acquire into this TRW Layer from OSM for 'My' Traces
3669 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3671 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3675 #ifdef VIK_CONFIG_GEOCACHES
3677 * Acquire into this TRW Layer from Geocaching.com
3679 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3681 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3685 #ifdef VIK_CONFIG_GEOTAG
3687 * Acquire into this TRW Layer from images
3689 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3691 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3693 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3695 // Reverify thumbnails as they may have changed
3696 vtl->has_verified_thumbnails = FALSE;
3697 trw_layer_verify_thumbnails ( vtl, NULL );
3702 * Acquire into this TRW Layer from any GPS Babel supported file
3704 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3706 trw_layer_acquire ( values, &vik_datasource_file_interface );
3709 static void trw_layer_gps_upload ( menu_array_layer values )
3711 menu_array_sublayer data;
3713 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3715 data[MA_VTL] = values[MA_VTL];
3716 data[MA_VLP] = values[MA_VLP];
3718 trw_layer_gps_upload_any ( data );
3722 * If pass_along[3] is defined that this will upload just that track
3724 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3726 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3727 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3729 // May not actually get a track here as values[2&3] can be null
3730 VikTrack *track = NULL;
3731 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3732 gboolean xfer_all = FALSE;
3734 if ( values[MA_SUBTYPE] ) {
3736 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3737 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3740 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3741 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3744 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3747 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3751 else if ( !values[MA_CONFIRM] )
3752 xfer_all = TRUE; // i.e. whole layer
3754 if (track && !track->visible) {
3755 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3759 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3760 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3761 GTK_DIALOG_DESTROY_WITH_PARENT,
3763 GTK_RESPONSE_ACCEPT,
3765 GTK_RESPONSE_REJECT,
3768 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3769 GtkWidget *response_w = NULL;
3770 #if GTK_CHECK_VERSION (2, 20, 0)
3771 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3775 gtk_widget_grab_focus ( response_w );
3777 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3779 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3780 datasource_gps_clean_up ( dgs );
3781 gtk_widget_destroy ( dialog );
3785 // Get info from reused datasource dialog widgets
3786 gchar* protocol = datasource_gps_get_protocol ( dgs );
3787 gchar* port = datasource_gps_get_descriptor ( dgs );
3788 // NB don't free the above strings as they're references to values held elsewhere
3789 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3790 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3791 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3792 gboolean turn_off = datasource_gps_get_off ( dgs );
3794 gtk_widget_destroy ( dialog );
3796 // When called from the viewport - work the corresponding layerspanel:
3798 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3801 // Apply settings to transfer to the GPS device
3808 vik_layers_panel_get_viewport (vlp),
3816 static void trw_layer_new_wp ( menu_array_layer values )
3818 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3819 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3820 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3821 instead return true if you want to update. */
3822 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 ) {
3823 trw_layer_calculate_bounds_waypoints ( vtl );
3824 vik_layers_panel_emit_update ( vlp );
3828 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3830 vtl->current_track = vik_track_new();
3831 vik_track_set_defaults ( vtl->current_track );
3832 vtl->current_track->visible = TRUE;
3833 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3834 // Create track with the preferred colour from the layer properties
3835 vtl->current_track->color = vtl->track_color;
3837 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3838 vtl->current_track->has_color = TRUE;
3839 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3842 static void trw_layer_new_track ( menu_array_layer values )
3844 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3846 if ( ! vtl->current_track ) {
3847 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3848 new_track_create_common ( vtl, name );
3851 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3855 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3857 vtl->current_track = vik_track_new();
3858 vik_track_set_defaults ( vtl->current_track );
3859 vtl->current_track->visible = TRUE;
3860 vtl->current_track->is_route = TRUE;
3861 // By default make all routes red
3862 vtl->current_track->has_color = TRUE;
3863 gdk_color_parse ( "red", &vtl->current_track->color );
3864 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3867 static void trw_layer_new_route ( menu_array_layer values )
3869 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3871 if ( ! vtl->current_track ) {
3872 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3873 new_route_create_common ( vtl, name );
3875 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3879 static void trw_layer_auto_routes_view ( menu_array_layer values )
3881 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3882 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3884 if ( g_hash_table_size (vtl->routes) > 0 ) {
3885 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3886 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3887 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3888 vik_layers_panel_emit_update ( vlp );
3893 static void trw_layer_finish_track ( menu_array_layer values )
3895 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3896 vtl->current_track = NULL;
3897 vtl->route_finder_started = FALSE;
3898 vik_layer_emit_update ( VIK_LAYER(vtl) );
3901 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3903 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3904 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3906 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3907 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3908 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3909 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3910 vik_layers_panel_emit_update ( vlp );
3914 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3916 /* NB do not care if wp is visible or not */
3917 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3920 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3922 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3923 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3925 /* Only 1 waypoint - jump straight to it */
3926 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3927 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3928 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3930 /* If at least 2 waypoints - find center and then zoom to fit */
3931 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3933 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3934 maxmin[0].lat = vtl->waypoints_bbox.north;
3935 maxmin[1].lat = vtl->waypoints_bbox.south;
3936 maxmin[0].lon = vtl->waypoints_bbox.east;
3937 maxmin[1].lon = vtl->waypoints_bbox.west;
3938 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3941 vik_layers_panel_emit_update ( vlp );
3944 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3946 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3949 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3951 if ( values[MA_MISC] ) {
3952 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3953 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3957 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3959 static menu_array_layer pass_along;
3961 GtkWidget *export_submenu;
3962 pass_along[MA_VTL] = vtl;
3963 pass_along[MA_VLP] = vlp;
3965 item = gtk_menu_item_new();
3966 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3967 gtk_widget_show ( item );
3969 if ( vtl->current_track ) {
3970 if ( vtl->current_track->is_route )
3971 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3973 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3975 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3976 gtk_widget_show ( item );
3979 item = gtk_menu_item_new ();
3980 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3981 gtk_widget_show ( item );
3984 /* Now with icons */
3985 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3988 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3989 gtk_widget_show ( item );
3991 GtkWidget *view_submenu = gtk_menu_new();
3992 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3993 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3994 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3995 gtk_widget_show ( item );
3996 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3998 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4000 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4001 gtk_widget_show ( item );
4003 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4005 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4006 gtk_widget_show ( item );
4008 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4010 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4011 gtk_widget_show ( item );
4013 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4016 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4017 gtk_widget_show ( item );
4019 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4021 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4022 gtk_widget_show ( item );
4024 export_submenu = gtk_menu_new ();
4025 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4026 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4027 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4028 gtk_widget_show ( item );
4029 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4031 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4033 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4034 gtk_widget_show ( item );
4036 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4038 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4039 gtk_widget_show ( item );
4041 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4043 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4044 gtk_widget_show ( item );
4046 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4048 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4049 gtk_widget_show ( item );
4051 if ( have_geojson_export ) {
4052 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4054 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4055 gtk_widget_show ( item );
4058 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4060 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4061 gtk_widget_show ( item );
4063 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4064 item = gtk_menu_item_new_with_mnemonic ( external1 );
4065 g_free ( external1 );
4066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4067 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4068 gtk_widget_show ( item );
4070 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4071 item = gtk_menu_item_new_with_mnemonic ( external2 );
4072 g_free ( external2 );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4074 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4075 gtk_widget_show ( item );
4077 GtkWidget *new_submenu = gtk_menu_new();
4078 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4079 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4080 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4081 gtk_widget_show(item);
4082 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4084 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4087 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4088 gtk_widget_show ( item );
4090 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4091 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4092 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4093 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4094 gtk_widget_show ( item );
4095 // Make it available only when a new track *not* already in progress
4096 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4098 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4100 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4101 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4102 gtk_widget_show ( item );
4103 // Make it available only when a new track *not* already in progress
4104 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4106 #ifdef VIK_CONFIG_GEOTAG
4107 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4109 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4110 gtk_widget_show ( item );
4113 GtkWidget *acquire_submenu = gtk_menu_new ();
4114 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4115 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4117 gtk_widget_show ( item );
4118 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4120 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4122 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4123 gtk_widget_show ( item );
4125 /* FIXME: only add menu when at least a routing engine has support for Directions */
4126 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4128 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4129 gtk_widget_show ( item );
4131 #ifdef VIK_CONFIG_OPENSTREETMAP
4132 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4134 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4135 gtk_widget_show ( item );
4137 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4139 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4140 gtk_widget_show ( item );
4143 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4145 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4146 gtk_widget_show ( item );
4148 #ifdef VIK_CONFIG_GEONAMES
4149 GtkWidget *wikipedia_submenu = gtk_menu_new();
4150 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4151 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4152 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4153 gtk_widget_show(item);
4154 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4156 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4159 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4160 gtk_widget_show ( item );
4162 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4165 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4166 gtk_widget_show ( item );
4169 #ifdef VIK_CONFIG_GEOCACHES
4170 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4172 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4173 gtk_widget_show ( item );
4176 #ifdef VIK_CONFIG_GEOTAG
4177 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4179 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4180 gtk_widget_show ( item );
4183 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4185 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4186 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4187 gtk_widget_show ( item );
4189 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4191 GtkWidget *upload_submenu = gtk_menu_new ();
4192 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4193 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4194 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4195 gtk_widget_show ( item );
4196 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4198 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4199 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4201 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4202 gtk_widget_show ( item );
4204 #ifdef VIK_CONFIG_OPENSTREETMAP
4205 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4208 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4209 gtk_widget_show ( item );
4212 GtkWidget *delete_submenu = gtk_menu_new ();
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4215 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4216 gtk_widget_show ( item );
4217 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4219 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4222 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4223 gtk_widget_show ( item );
4225 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4228 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4229 gtk_widget_show ( item );
4231 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4232 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4233 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4234 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4235 gtk_widget_show ( item );
4237 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4238 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4239 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4240 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4241 gtk_widget_show ( item );
4243 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4244 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4246 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4247 gtk_widget_show ( item );
4249 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4250 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4251 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4252 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4253 gtk_widget_show ( item );
4255 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4256 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4258 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4259 gtk_widget_show ( item );
4262 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4263 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4265 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4266 gtk_widget_show ( item );
4269 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
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_track_list_dialog), pass_along );
4272 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4273 gtk_widget_show ( item );
4274 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4276 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4279 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4280 gtk_widget_show ( item );
4281 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4284 // Fake Waypoint UUIDs vi simple increasing integer
4285 static guint wp_uuid = 0;
4287 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4291 vik_waypoint_set_name (wp, name);
4293 if ( VIK_LAYER(vtl)->realized )
4295 // Do we need to create the sublayer:
4296 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4297 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4300 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4302 // Visibility column always needed for waypoints
4303 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 );
4305 // Actual setting of visibility dependent on the waypoint
4306 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4308 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4310 // Sort now as post_read is not called on a realized waypoint
4311 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4314 highest_wp_number_add_wp(vtl, name);
4315 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4319 // Fake Track UUIDs vi simple increasing integer
4320 static guint tr_uuid = 0;
4322 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4326 vik_track_set_name (t, name);
4328 if ( VIK_LAYER(vtl)->realized )
4330 // Do we need to create the sublayer:
4331 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4332 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4335 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4336 // Visibility column always needed for tracks
4337 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 );
4339 // Actual setting of visibility dependent on the track
4340 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4342 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4344 // Sort now as post_read is not called on a realized track
4345 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4348 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4350 trw_layer_update_treeview ( vtl, t );
4353 // Fake Route UUIDs vi simple increasing integer
4354 static guint rt_uuid = 0;
4356 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4360 vik_track_set_name (t, name);
4362 if ( VIK_LAYER(vtl)->realized )
4364 // Do we need to create the sublayer:
4365 if ( g_hash_table_size (vtl->routes) == 0 ) {
4366 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4369 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4370 // Visibility column always needed for routes
4371 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 );
4372 // Actual setting of visibility dependent on the route
4373 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4375 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4377 // Sort now as post_read is not called on a realized route
4378 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4381 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4383 trw_layer_update_treeview ( vtl, t );
4386 /* to be called whenever a track has been deleted or may have been changed. */
4387 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4389 if (vtl->current_tp_track == trk )
4390 trw_layer_cancel_current_tp ( vtl, FALSE );
4394 * Normally this is done to due the waypoint size preference having changed
4396 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4398 GHashTableIter iter;
4399 gpointer key, value;
4402 g_hash_table_iter_init ( &iter, vtl->waypoints );
4403 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4404 VikWaypoint *wp = VIK_WAYPOINT(value);
4406 // Reapply symbol setting to update the pixbuf
4407 gchar *tmp_symbol = g_strdup ( wp->symbol );
4408 vik_waypoint_set_symbol ( wp, tmp_symbol );
4409 g_free ( tmp_symbol );
4415 * trw_layer_new_unique_sublayer_name:
4417 * Allocates a unique new name
4419 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4422 gchar *newname = g_strdup(name);
4427 switch ( sublayer_type ) {
4428 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4429 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4431 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4432 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4435 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4438 // If found a name already in use try adding 1 to it and we try again
4440 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4442 newname = new_newname;
4445 } while ( id != NULL);
4450 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4452 // No more uniqueness of name forced when loading from a file
4453 // This now makes this function a little redunant as we just flow the parameters through
4454 vik_trw_layer_add_waypoint ( vtl, name, wp );
4457 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4459 if ( vtl->route_finder_append && vtl->current_track ) {
4460 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4462 // enforce end of current track equal to start of tr
4463 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4464 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4465 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4466 vik_track_add_trackpoint ( vtl->current_track,
4467 vik_trackpoint_copy ( cur_end ),
4471 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4472 vik_track_free ( tr );
4473 vtl->route_finder_append = FALSE; /* this means we have added it */
4476 // No more uniqueness of name forced when loading from a file
4478 vik_trw_layer_add_route ( vtl, name, tr );
4480 vik_trw_layer_add_track ( vtl, name, tr );
4482 if ( vtl->route_finder_check_added_track ) {
4483 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4484 vtl->route_finder_added_track = tr;
4489 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4491 *l = g_list_append(*l, id);
4495 * Move an item from one TRW layer to another TRW layer
4497 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4499 // TODO reconsider strategy when moving within layer (if anything...)
4500 gboolean rename = ( vtl_src != vtl_dest );
4504 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4505 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4509 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4511 newname = g_strdup ( trk->name );
4513 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4514 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4516 vik_trw_layer_delete_track ( vtl_src, trk );
4519 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4520 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4524 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4526 newname = g_strdup ( trk->name );
4528 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4529 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4531 vik_trw_layer_delete_route ( vtl_src, trk );
4534 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4535 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4539 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4541 newname = g_strdup ( wp->name );
4543 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4544 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4546 trw_layer_delete_waypoint ( vtl_src, wp );
4548 // Recalculate bounds even if not renamed as maybe dragged between layers
4549 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4550 trw_layer_calculate_bounds_waypoints ( vtl_src );
4554 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4556 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4557 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4559 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4560 GList *items = NULL;
4563 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4564 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4566 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4567 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4569 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4570 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4575 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4576 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4577 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4578 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4580 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4587 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4588 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4592 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4594 trku_udata *user_data = udata;
4595 if ( trk == user_data->trk ) {
4596 user_data->uuid = id;
4602 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4604 gboolean was_visible = FALSE;
4605 if ( trk && trk->name ) {
4607 if ( trk == vtl->current_track ) {
4608 vtl->current_track = NULL;
4609 vtl->current_tp_track = NULL;
4610 vtl->current_tp_id = NULL;
4611 vtl->moving_tp = FALSE;
4612 vtl->route_finder_started = FALSE;
4615 was_visible = trk->visible;
4617 if ( trk == vtl->route_finder_added_track )
4618 vtl->route_finder_added_track = NULL;
4624 // Hmmm, want key of it
4625 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4627 if ( trkf && udata.uuid ) {
4628 /* could be current_tp, so we have to check */
4629 trw_layer_cancel_tps_of_track ( vtl, trk );
4631 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4634 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4635 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4636 g_hash_table_remove ( vtl->tracks, udata.uuid );
4638 // If last sublayer, then remove sublayer container
4639 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4640 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4643 // Incase it was selected (no item delete signal ATM)
4644 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4650 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4652 gboolean was_visible = FALSE;
4654 if ( trk && trk->name ) {
4656 if ( trk == vtl->current_track ) {
4657 vtl->current_track = NULL;
4658 vtl->current_tp_track = NULL;
4659 vtl->current_tp_id = NULL;
4660 vtl->moving_tp = FALSE;
4663 was_visible = trk->visible;
4665 if ( trk == vtl->route_finder_added_track )
4666 vtl->route_finder_added_track = NULL;
4672 // Hmmm, want key of it
4673 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4675 if ( trkf && udata.uuid ) {
4676 /* could be current_tp, so we have to check */
4677 trw_layer_cancel_tps_of_track ( vtl, trk );
4679 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4682 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4683 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4684 g_hash_table_remove ( vtl->routes, udata.uuid );
4686 // If last sublayer, then remove sublayer container
4687 if ( g_hash_table_size (vtl->routes) == 0 ) {
4688 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4691 // Incase it was selected (no item delete signal ATM)
4692 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4698 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4700 gboolean was_visible = FALSE;
4702 if ( wp && wp->name ) {
4704 if ( wp == vtl->current_wp ) {
4705 vtl->current_wp = NULL;
4706 vtl->current_wp_id = NULL;
4707 vtl->moving_wp = FALSE;
4710 was_visible = wp->visible;
4716 // Hmmm, want key of it
4717 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4719 if ( wpf && udata.uuid ) {
4720 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4723 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4724 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4726 highest_wp_number_remove_wp(vtl, wp->name);
4727 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4729 // If last sublayer, then remove sublayer container
4730 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4731 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4734 // Incase it was selected (no item delete signal ATM)
4735 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4743 // Only for temporary use by trw_layer_delete_waypoint_by_name
4744 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4746 wpu_udata *user_data = udata;
4747 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4748 user_data->uuid = id;
4755 * Delete a waypoint by the given name
4756 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4757 * as there be multiple waypoints with the same name
4759 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4762 // Fake a waypoint with the given name
4763 udata.wp = vik_waypoint_new ();
4764 vik_waypoint_set_name (udata.wp, name);
4765 // Currently only the name is used in this waypoint find function
4768 // Hmmm, want key of it
4769 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4771 vik_waypoint_free (udata.wp);
4773 if ( wpf && udata.uuid )
4774 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4780 VikTrack *trk; // input
4781 gpointer uuid; // output
4784 // Only for temporary use by trw_layer_delete_track_by_name
4785 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4787 tpu_udata *user_data = udata;
4788 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4789 user_data->uuid = id;
4796 * Delete a track by the given name
4797 * NOTE: ATM this will delete the first encountered Track with the specified name
4798 * as there may be multiple tracks with the same name within the specified hash table
4800 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4803 // Fake a track with the given name
4804 udata.trk = vik_track_new ();
4805 vik_track_set_name (udata.trk, name);
4806 // Currently only the name is used in this waypoint find function
4809 // Hmmm, want key of it
4810 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4812 vik_track_free (udata.trk);
4814 if ( trkf && udata.uuid ) {
4815 // This could be a little better written...
4816 if ( vtl->tracks == ht_tracks )
4817 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4818 if ( vtl->routes == ht_tracks )
4819 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4826 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4828 vik_treeview_item_delete (vt, it );
4831 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4834 vtl->current_track = NULL;
4835 vtl->route_finder_added_track = NULL;
4836 if (vtl->current_tp_track)
4837 trw_layer_cancel_current_tp(vtl, FALSE);
4839 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4840 g_hash_table_remove_all(vtl->routes_iters);
4841 g_hash_table_remove_all(vtl->routes);
4843 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4845 vik_layer_emit_update ( VIK_LAYER(vtl) );
4848 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4851 vtl->current_track = NULL;
4852 vtl->route_finder_added_track = NULL;
4853 if (vtl->current_tp_track)
4854 trw_layer_cancel_current_tp(vtl, FALSE);
4856 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4857 g_hash_table_remove_all(vtl->tracks_iters);
4858 g_hash_table_remove_all(vtl->tracks);
4860 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4862 vik_layer_emit_update ( VIK_LAYER(vtl) );
4865 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4867 vtl->current_wp = NULL;
4868 vtl->current_wp_id = NULL;
4869 vtl->moving_wp = FALSE;
4871 highest_wp_number_reset(vtl);
4873 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4874 g_hash_table_remove_all(vtl->waypoints_iters);
4875 g_hash_table_remove_all(vtl->waypoints);
4877 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4879 vik_layer_emit_update ( VIK_LAYER(vtl) );
4882 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4884 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4885 // Get confirmation from the user
4886 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4887 _("Are you sure you want to delete all tracks in %s?"),
4888 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4889 vik_trw_layer_delete_all_tracks (vtl);
4892 static void trw_layer_delete_all_routes ( menu_array_layer values )
4894 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4895 // Get confirmation from the user
4896 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4897 _("Are you sure you want to delete all routes in %s?"),
4898 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4899 vik_trw_layer_delete_all_routes (vtl);
4902 static void trw_layer_delete_all_waypoints ( 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 waypoints in %s?"),
4908 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4909 vik_trw_layer_delete_all_waypoints (vtl);
4912 static void trw_layer_delete_item ( menu_array_sublayer values )
4914 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4915 gboolean was_visible = FALSE;
4916 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4918 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4919 if ( wp && wp->name ) {
4920 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4921 // Get confirmation from the user
4922 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4923 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4924 _("Are you sure you want to delete the waypoint \"%s\"?"),
4927 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4930 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4932 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4933 if ( trk && trk->name ) {
4934 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4935 // Get confirmation from the user
4936 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4937 _("Are you sure you want to delete the track \"%s\"?"),
4940 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4945 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4946 if ( trk && trk->name ) {
4947 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4948 // Get confirmation from the user
4949 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4950 _("Are you sure you want to delete the route \"%s\"?"),
4953 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4957 vik_layer_emit_update ( VIK_LAYER(vtl) );
4961 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4963 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4965 vik_waypoint_set_name ( wp, new_name );
4967 // Now update the treeview as well
4972 // Need key of it for treeview update
4973 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4975 if ( wpf && udataU.uuid ) {
4976 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4979 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4980 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4986 * Maintain icon of waypoint in the treeview
4988 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4990 // update the treeview
4995 // Need key of it for treeview update
4996 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4998 if ( wpf && udataU.uuid ) {
4999 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5002 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5007 static void trw_layer_properties_item ( menu_array_sublayer values )
5009 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5010 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5012 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5014 if ( wp && wp->name )
5016 gboolean updated = FALSE;
5017 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5019 trw_layer_waypoint_rename ( vtl, wp, new_name );
5021 if ( updated && values[MA_TV_ITER] )
5022 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5024 if ( updated && VIK_LAYER(vtl)->visible )
5025 vik_layer_emit_update ( VIK_LAYER(vtl) );
5031 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5032 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5034 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5036 if ( tr && tr->name )
5038 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5049 * trw_layer_track_statistics:
5051 * Show track statistics.
5052 * ATM jump to the stats page in the properties
5053 * TODO: consider separating the stats into an individual dialog?
5055 static void trw_layer_track_statistics ( menu_array_sublayer values )
5057 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5059 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5060 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5062 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5064 if ( trk && trk->name ) {
5065 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5075 * Update the treeview of the track id - primarily to update the icon
5077 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5083 gpointer *trkf = NULL;
5084 if ( trk->is_route )
5085 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5087 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5089 if ( trkf && udata.uuid ) {
5091 GtkTreeIter *iter = NULL;
5092 if ( trk->is_route )
5093 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5095 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5098 // TODO: Make this a function
5099 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5100 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5101 ((trk->color.green & 0xff00) << 8) |
5102 (trk->color.blue & 0xff00);
5103 gdk_pixbuf_fill ( pixbuf, pixel );
5104 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5105 g_object_unref (pixbuf);
5112 Parameter 1 -> VikLayersPanel
5113 Parameter 2 -> VikLayer
5114 Parameter 3 -> VikViewport
5116 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5119 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5120 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5123 /* since vlp not set, vl & vvp should be valid instead! */
5125 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5126 vik_layer_emit_update ( VIK_LAYER(vl) );
5131 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5133 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5135 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5136 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5138 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5140 if ( track && track->trackpoints )
5141 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5144 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5146 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5148 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5149 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5151 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5153 if ( track && track->trackpoints )
5155 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5157 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5158 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5159 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5160 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5161 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5165 static void trw_layer_convert_track_route ( 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 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5172 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5177 // Converting a track to a route can be a bit more complicated,
5178 // so give a chance to change our minds:
5179 if ( !trk->is_route &&
5180 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5181 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5183 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5184 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5189 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5192 trk_copy->is_route = !trk_copy->is_route;
5194 // ATM can't set name to self - so must create temporary copy
5195 gchar *name = g_strdup ( trk_copy->name );
5197 // Delete old one and then add new one
5198 if ( trk->is_route ) {
5199 vik_trw_layer_delete_route ( vtl, trk );
5200 vik_trw_layer_add_track ( vtl, name, trk_copy );
5203 // Extra route conversion bits...
5204 vik_track_merge_segments ( trk_copy );
5205 vik_track_to_routepoints ( trk_copy );
5207 vik_trw_layer_delete_track ( vtl, trk );
5208 vik_trw_layer_add_route ( vtl, name, trk_copy );
5212 // Update in case color of track / route changes when moving between sublayers
5213 vik_layer_emit_update ( VIK_LAYER(vtl) );
5216 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5218 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5220 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5221 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5223 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5226 vik_track_anonymize_times ( track );
5229 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5231 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5233 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5234 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5236 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5241 vtl->current_track = track;
5242 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);
5244 if ( track->trackpoints )
5245 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5249 * extend a track using route finder
5251 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5253 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5254 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5258 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5259 vtl->current_track = track;
5260 vtl->route_finder_started = TRUE;
5262 if ( track->trackpoints )
5263 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5269 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5271 // If have a vlp then perform a basic test to see if any DEM info available...
5273 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5275 if ( !g_list_length(dems) ) {
5276 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5284 * apply_dem_data_common:
5286 * A common function for applying the DEM values and reporting the results.
5288 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5290 if ( !trw_layer_dem_test ( vtl, vlp ) )
5293 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5294 // Inform user how much was changed
5296 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5297 g_snprintf(str, 64, tmp_str, changed);
5298 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5301 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5303 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5305 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5306 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5308 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5311 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5314 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5316 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5318 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5319 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5321 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5324 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5330 * A common function for applying the elevation smoothing and reporting the results.
5332 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5334 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5335 // Inform user how much was changed
5337 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5338 g_snprintf(str, 64, tmp_str, changed);
5339 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5345 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5347 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5349 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5350 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5352 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5357 smooth_it ( vtl, track, FALSE );
5360 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5362 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5364 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5365 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5367 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5372 smooth_it ( vtl, track, TRUE );
5376 * Commonal helper function
5378 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5381 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5382 g_snprintf(str, 64, tmp_str, changed);
5383 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5386 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5388 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5389 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5391 if ( !trw_layer_dem_test ( vtl, vlp ) )
5395 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5397 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5399 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5403 GHashTableIter iter;
5404 gpointer key, value;
5406 g_hash_table_iter_init ( &iter, vtl->waypoints );
5407 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5408 VikWaypoint *wp = VIK_WAYPOINT(value);
5409 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5412 wp_changed_message ( vtl, changed );
5415 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5417 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5418 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5420 if ( !trw_layer_dem_test ( vtl, vlp ) )
5424 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5426 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5428 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5432 GHashTableIter iter;
5433 gpointer key, value;
5435 g_hash_table_iter_init ( &iter, vtl->waypoints );
5436 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5437 VikWaypoint *wp = VIK_WAYPOINT(value);
5438 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5441 wp_changed_message ( vtl, changed );
5444 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5446 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5448 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5449 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5451 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5455 if ( !track->trackpoints )
5457 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5460 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5462 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5464 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5472 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5475 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5478 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5480 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5482 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5483 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5485 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5490 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5493 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5496 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5498 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5500 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5501 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5503 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5508 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5511 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5515 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5517 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5519 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5521 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5522 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5524 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5526 if ( trk && trk->trackpoints )
5528 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5529 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5530 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5531 if ( values[MA_VLP] )
5532 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5534 vik_layer_emit_update ( VIK_LAYER(vtl) );
5539 * Refine the selected track/route with a routing engine.
5540 * The routing engine is selected by the user, when requestiong the job.
5542 static void trw_layer_route_refine ( menu_array_sublayer values )
5544 static gint last_engine = 0;
5545 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5548 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5549 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5551 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5553 if ( trk && trk->trackpoints )
5555 /* Check size of the route */
5556 int nb = vik_track_get_tp_count(trk);
5558 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5559 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5560 GTK_MESSAGE_WARNING,
5561 GTK_BUTTONS_OK_CANCEL,
5562 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5564 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5565 gtk_widget_destroy ( dialog );
5566 if (response != GTK_RESPONSE_OK )
5569 /* Select engine from dialog */
5570 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5571 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5572 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5574 GTK_RESPONSE_REJECT,
5576 GTK_RESPONSE_ACCEPT,
5578 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5579 gtk_widget_show_all(label);
5581 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5583 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5584 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5585 gtk_widget_show_all(combo);
5587 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5589 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5591 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5593 /* Dialog validated: retrieve selected engine and do the job */
5594 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5595 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5598 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5600 /* Force saving track */
5601 /* FIXME: remove or rename this hack */
5602 vtl->route_finder_check_added_track = TRUE;
5605 vik_routing_engine_refine (routing, vtl, trk);
5607 /* FIXME: remove or rename this hack */
5608 if ( vtl->route_finder_added_track )
5609 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5611 vtl->route_finder_added_track = NULL;
5612 vtl->route_finder_check_added_track = FALSE;
5614 vik_layer_emit_update ( VIK_LAYER(vtl) );
5616 /* Restore cursor */
5617 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5619 gtk_widget_destroy ( dialog );
5623 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5625 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5626 trw_layer_tpwin_init ( vtl );
5629 /*************************************
5630 * merge/split by time routines
5631 *************************************/
5633 /* called for each key in track hash table.
5634 * If the current track has the same time stamp type, add it to the result,
5635 * except the one pointed by "exclude".
5636 * set exclude to NULL if there is no exclude to check.
5637 * Note that the result is in reverse (for performance reasons).
5642 gboolean with_timestamps;
5644 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5646 twt_udata *user_data = udata;
5647 VikTrackpoint *p1, *p2;
5648 VikTrack *trk = VIK_TRACK(value);
5649 if (trk == user_data->exclude) {
5653 if (trk->trackpoints) {
5654 p1 = vik_track_get_tp_first(trk);
5655 p2 = vik_track_get_tp_last(trk);
5657 if ( user_data->with_timestamps ) {
5658 if (!p1->has_timestamp || !p2->has_timestamp) {
5663 // Don't add tracks with timestamps when getting non timestamp tracks
5664 if (p1->has_timestamp || p2->has_timestamp) {
5670 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5674 * find_nearby_tracks_by_time:
5676 * Called for each track in track hash table.
5677 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5678 * to the current track, then the current track is added to the list in user_data[0]
5680 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5682 VikTrack *trk = VIK_TRACK(value);
5684 GList **nearby_tracks = ((gpointer *)user_data)[0];
5685 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5687 if ( !orig_trk || !orig_trk->trackpoints )
5691 * detect reasons for not merging, and return
5692 * if no reason is found not to merge, then do it.
5695 twt_udata *udata = user_data;
5696 // Exclude the original track from the compiled list
5697 if (trk == udata->exclude) {
5701 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5702 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5704 if (trk->trackpoints) {
5706 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5707 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5709 if (!p1->has_timestamp || !p2->has_timestamp) {
5710 //g_print("no timestamp\n");
5714 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5715 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5716 if (! (abs(t1 - p2->timestamp) < threshold ||
5718 abs(p1->timestamp - t2) < threshold)
5725 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5728 /* comparison function used to sort tracks; a and b are hash table keys */
5729 /* Not actively used - can be restored if needed
5730 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5732 GHashTable *tracks = user_data;
5735 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5736 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5738 if (t1 < t2) return -1;
5739 if (t1 > t2) return 1;
5744 /* comparison function used to sort trackpoints */
5745 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5747 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5749 if (t1 < t2) return -1;
5750 if (t1 > t2) return 1;
5755 * comparison function which can be used to sort tracks or waypoints by name
5757 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5759 const gchar* namea = (const gchar*) a;
5760 const gchar* nameb = (const gchar*) b;
5761 if ( namea == NULL || nameb == NULL)
5764 // Same sort method as used in the vik_treeview_*_alphabetize functions
5765 return strcmp ( namea, nameb );
5769 * Attempt to merge selected track with other tracks specified by the user
5770 * Tracks to merge with must be of the same 'type' as the selected track -
5771 * either all with timestamps, or all without timestamps
5773 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5775 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5776 GList *other_tracks = NULL;
5777 GHashTable *ght_tracks;
5778 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5779 ght_tracks = vtl->routes;
5781 ght_tracks = vtl->tracks;
5783 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5788 if ( !track->trackpoints )
5792 udata.result = &other_tracks;
5793 udata.exclude = track;
5794 // Allow merging with 'similar' time type time tracks
5795 // i.e. either those times, or those without
5796 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5798 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5799 other_tracks = g_list_reverse(other_tracks);
5801 if ( !other_tracks ) {
5802 if ( udata.with_timestamps )
5803 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5805 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5809 // Sort alphabetically for user presentation
5810 // Convert into list of names for usage with dialog function
5811 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5812 GList *other_tracks_names = NULL;
5813 GList *iter = g_list_first ( other_tracks );
5815 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5816 iter = g_list_next ( iter );
5819 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5821 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5825 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5826 g_list_free(other_tracks);
5827 g_list_free(other_tracks_names);
5832 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5833 VikTrack *merge_track;
5834 if ( track->is_route )
5835 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5837 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5840 vik_track_steal_and_append_trackpoints ( track, merge_track );
5841 if ( track->is_route )
5842 vik_trw_layer_delete_route (vtl, merge_track);
5844 vik_trw_layer_delete_track (vtl, merge_track);
5845 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5848 for (l = merge_list; l != NULL; l = g_list_next(l))
5850 g_list_free(merge_list);
5852 vik_layer_emit_update( VIK_LAYER(vtl) );
5856 // c.f. trw_layer_sorted_track_id_by_name_list
5857 // but don't add the specified track to the list (normally current track)
5858 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5860 twt_udata *user_data = udata;
5863 if (trk == user_data->exclude) {
5867 // Sort named list alphabetically
5868 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5872 * Join - this allows combining 'tracks' and 'track routes'
5873 * i.e. doesn't care about whether tracks have consistent timestamps
5874 * ATM can only append one track at a time to the currently selected track
5876 static void trw_layer_append_track ( menu_array_sublayer values )
5879 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5881 GHashTable *ght_tracks;
5882 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5883 ght_tracks = vtl->routes;
5885 ght_tracks = vtl->tracks;
5887 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5892 GList *other_tracks_names = NULL;
5894 // Sort alphabetically for user presentation
5895 // Convert into list of names for usage with dialog function
5896 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5898 udata.result = &other_tracks_names;
5899 udata.exclude = trk;
5901 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5903 // Note the limit to selecting one track only
5904 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5905 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5906 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5909 trk->is_route ? _("Append Route"): _("Append Track"),
5910 trk->is_route ? _("Select the route to append after the current route") :
5911 _("Select the track to append after the current track") );
5913 g_list_free(other_tracks_names);
5915 // It's a list, but shouldn't contain more than one other track!
5916 if ( append_list ) {
5918 for (l = append_list; l != NULL; l = g_list_next(l)) {
5919 // TODO: at present this uses the first track found by name,
5920 // which with potential multiple same named tracks may not be the one selected...
5921 VikTrack *append_track;
5922 if ( trk->is_route )
5923 append_track = vik_trw_layer_get_route ( vtl, l->data );
5925 append_track = vik_trw_layer_get_track ( vtl, l->data );
5927 if ( append_track ) {
5928 vik_track_steal_and_append_trackpoints ( trk, append_track );
5929 if ( trk->is_route )
5930 vik_trw_layer_delete_route (vtl, append_track);
5932 vik_trw_layer_delete_track (vtl, append_track);
5935 for (l = append_list; l != NULL; l = g_list_next(l))
5937 g_list_free(append_list);
5939 vik_layer_emit_update( VIK_LAYER(vtl) );
5944 * Very similar to trw_layer_append_track for joining
5945 * but this allows selection from the 'other' list
5946 * If a track is selected, then is shows routes and joins the selected one
5947 * If a route is selected, then is shows tracks and joins the selected one
5949 static void trw_layer_append_other ( menu_array_sublayer values )
5952 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5954 GHashTable *ght_mykind, *ght_others;
5955 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5956 ght_mykind = vtl->routes;
5957 ght_others = vtl->tracks;
5960 ght_mykind = vtl->tracks;
5961 ght_others = vtl->routes;
5964 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5969 GList *other_tracks_names = NULL;
5971 // Sort alphabetically for user presentation
5972 // Convert into list of names for usage with dialog function
5973 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5975 udata.result = &other_tracks_names;
5976 udata.exclude = trk;
5978 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5980 // Note the limit to selecting one track only
5981 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5982 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5983 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5986 trk->is_route ? _("Append Track"): _("Append Route"),
5987 trk->is_route ? _("Select the track to append after the current route") :
5988 _("Select the route to append after the current track") );
5990 g_list_free(other_tracks_names);
5992 // It's a list, but shouldn't contain more than one other track!
5993 if ( append_list ) {
5995 for (l = append_list; l != NULL; l = g_list_next(l)) {
5996 // TODO: at present this uses the first track found by name,
5997 // which with potential multiple same named tracks may not be the one selected...
5999 // Get FROM THE OTHER TYPE list
6000 VikTrack *append_track;
6001 if ( trk->is_route )
6002 append_track = vik_trw_layer_get_track ( vtl, l->data );
6004 append_track = vik_trw_layer_get_route ( vtl, l->data );
6006 if ( append_track ) {
6008 if ( !append_track->is_route &&
6009 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6010 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6012 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6013 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6014 vik_track_merge_segments ( append_track );
6015 vik_track_to_routepoints ( append_track );
6022 vik_track_steal_and_append_trackpoints ( trk, append_track );
6024 // Delete copied which is FROM THE OTHER TYPE list
6025 if ( trk->is_route )
6026 vik_trw_layer_delete_track (vtl, append_track);
6028 vik_trw_layer_delete_route (vtl, append_track);
6031 for (l = append_list; l != NULL; l = g_list_next(l))
6033 g_list_free(append_list);
6034 vik_layer_emit_update( VIK_LAYER(vtl) );
6038 /* merge by segments */
6039 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6041 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6042 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6043 guint segments = vik_track_merge_segments ( trk );
6044 // NB currently no need to redraw as segments not actually shown on the display
6045 // However inform the user of what happened:
6047 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6048 g_snprintf(str, 64, tmp_str, segments);
6049 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6052 /* merge by time routine */
6053 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6055 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6059 GList *tracks_with_timestamp = NULL;
6060 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6061 if (orig_trk->trackpoints &&
6062 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6063 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6068 udata.result = &tracks_with_timestamp;
6069 udata.exclude = orig_trk;
6070 udata.with_timestamps = TRUE;
6071 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6072 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6074 if (!tracks_with_timestamp) {
6075 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6078 g_list_free(tracks_with_timestamp);
6080 static guint threshold_in_minutes = 1;
6081 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6082 _("Merge Threshold..."),
6083 _("Merge when time between tracks less than:"),
6084 &threshold_in_minutes)) {
6088 // keep attempting to merge all tracks until no merges within the time specified is possible
6089 gboolean attempt_merge = TRUE;
6090 GList *nearby_tracks = NULL;
6092 static gpointer params[3];
6094 while ( attempt_merge ) {
6096 // Don't try again unless tracks have changed
6097 attempt_merge = FALSE;
6099 trps = orig_trk->trackpoints;
6103 if (nearby_tracks) {
6104 g_list_free(nearby_tracks);
6105 nearby_tracks = NULL;
6108 params[0] = &nearby_tracks;
6109 params[1] = orig_trk;
6110 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6112 /* get a list of adjacent-in-time tracks */
6113 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6116 GList *l = nearby_tracks;
6118 /* remove trackpoints from merged track, delete track */
6119 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6120 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6122 // Tracks have changed, therefore retry again against all the remaining tracks
6123 attempt_merge = TRUE;
6128 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6131 g_list_free(nearby_tracks);
6133 vik_layer_emit_update( VIK_LAYER(vtl) );
6137 * Split a track at the currently selected trackpoint
6139 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6141 if ( !vtl->current_tpl )
6144 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6145 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6147 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6148 GList *newglist = g_list_alloc ();
6149 newglist->prev = NULL;
6150 newglist->next = vtl->current_tpl->next;
6151 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6152 tr->trackpoints = newglist;
6154 vtl->current_tpl->next->prev = newglist; /* end old track here */
6155 vtl->current_tpl->next = NULL;
6157 // Bounds of the selected track changed due to the split
6158 vik_track_calculate_bounds ( vtl->current_tp_track );
6160 vtl->current_tpl = newglist; /* change tp to first of new track. */
6161 vtl->current_tp_track = tr;
6164 vik_trw_layer_add_route ( vtl, name, tr );
6166 vik_trw_layer_add_track ( vtl, name, tr );
6168 // Bounds of the new track created by the split
6169 vik_track_calculate_bounds ( tr );
6175 // Also need id of newly created track
6178 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6180 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6182 if ( trkf && udata.uuid )
6183 vtl->current_tp_id = udata.uuid;
6185 vtl->current_tp_id = NULL;
6187 vik_layer_emit_update(VIK_LAYER(vtl));
6193 /* split by time routine */
6194 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6196 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6197 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6198 GList *trps = track->trackpoints;
6200 GList *newlists = NULL;
6201 GList *newtps = NULL;
6202 static guint thr = 1;
6209 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6210 _("Split Threshold..."),
6211 _("Split when time between trackpoints exceeds:"),
6216 /* iterate through trackpoints, and copy them into new lists without touching original list */
6217 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6221 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6223 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6226 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6227 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6228 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6230 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6235 if (ts - prev_ts > thr*60) {
6236 /* flush accumulated trackpoints into new list */
6237 newlists = g_list_append(newlists, g_list_reverse(newtps));
6241 /* accumulate trackpoint copies in newtps, in reverse order */
6242 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6244 iter = g_list_next(iter);
6247 newlists = g_list_append(newlists, g_list_reverse(newtps));
6250 /* put lists of trackpoints into tracks */
6252 // Only bother updating if the split results in new tracks
6253 if (g_list_length (newlists) > 1) {
6258 tr = vik_track_copy ( track, FALSE );
6259 tr->trackpoints = (GList *)(iter->data);
6261 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6262 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6263 g_free ( new_tr_name );
6264 vik_track_calculate_bounds ( tr );
6265 iter = g_list_next(iter);
6267 // Remove original track and then update the display
6268 vik_trw_layer_delete_track (vtl, track);
6269 vik_layer_emit_update(VIK_LAYER(vtl));
6271 g_list_free(newlists);
6275 * Split a track by the number of points as specified by the user
6277 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6279 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6281 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6282 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6284 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6289 // Check valid track
6290 GList *trps = track->trackpoints;
6294 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6295 _("Split Every Nth Point"),
6296 _("Split on every Nth point:"),
6297 250, // Default value as per typical limited track capacity of various GPS devices
6301 // Was a valid number returned?
6307 GList *newlists = NULL;
6308 GList *newtps = NULL;
6313 /* accumulate trackpoint copies in newtps, in reverse order */
6314 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6316 if (count >= points) {
6317 /* flush accumulated trackpoints into new list */
6318 newlists = g_list_append(newlists, g_list_reverse(newtps));
6322 iter = g_list_next(iter);
6325 // If there is a remaining chunk put that into the new split list
6326 // This may well be the whole track if no split points were encountered
6328 newlists = g_list_append(newlists, g_list_reverse(newtps));
6331 /* put lists of trackpoints into tracks */
6333 // Only bother updating if the split results in new tracks
6334 if (g_list_length (newlists) > 1) {
6339 tr = vik_track_copy ( track, FALSE );
6340 tr->trackpoints = (GList *)(iter->data);
6342 if ( track->is_route ) {
6343 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6344 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6347 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6348 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6350 g_free ( new_tr_name );
6351 vik_track_calculate_bounds ( tr );
6353 iter = g_list_next(iter);
6355 // Remove original track and then update the display
6356 if ( track->is_route )
6357 vik_trw_layer_delete_route (vtl, track);
6359 vik_trw_layer_delete_track (vtl, track);
6360 vik_layer_emit_update(VIK_LAYER(vtl));
6362 g_list_free(newlists);
6366 * Split a track at the currently selected trackpoint
6368 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6370 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6371 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6372 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6376 * Split a track by its segments
6377 * Routes do not have segments so don't call this for routes
6379 static void trw_layer_split_segments ( menu_array_sublayer values )
6381 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6382 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6389 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6392 for ( i = 0; i < ntracks; i++ ) {
6394 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6395 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6396 g_free ( new_tr_name );
6401 // Remove original track
6402 vik_trw_layer_delete_track ( vtl, trk );
6403 vik_layer_emit_update ( VIK_LAYER(vtl) );
6406 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6409 /* end of split/merge routines */
6411 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6415 // Find available adjacent trackpoint
6416 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6417 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6418 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6420 // Delete current trackpoint
6421 vik_trackpoint_free ( vtl->current_tpl->data );
6422 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6424 // Set to current to the available adjacent trackpoint
6425 vtl->current_tpl = new_tpl;
6427 if ( vtl->current_tp_track ) {
6428 vik_track_calculate_bounds ( vtl->current_tp_track );
6432 // Delete current trackpoint
6433 vik_trackpoint_free ( vtl->current_tpl->data );
6434 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6435 trw_layer_cancel_current_tp ( vtl, FALSE );
6440 * Delete the selected point
6442 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6444 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6446 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6447 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6449 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6454 if ( !vtl->current_tpl )
6457 trw_layer_trackpoint_selected_delete ( vtl, trk );
6459 // Track has been updated so update tps:
6460 trw_layer_cancel_tps_of_track ( vtl, trk );
6462 vik_layer_emit_update ( VIK_LAYER(vtl) );
6466 * Delete adjacent track points at the same position
6467 * AKA Delete Dulplicates on the Properties Window
6469 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6471 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6473 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6474 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6476 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6481 gulong removed = vik_track_remove_dup_points ( trk );
6483 // Track has been updated so update tps:
6484 trw_layer_cancel_tps_of_track ( vtl, trk );
6486 // Inform user how much was deleted as it's not obvious from the normal view
6488 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6489 g_snprintf(str, 64, tmp_str, removed);
6490 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6492 vik_layer_emit_update ( VIK_LAYER(vtl) );
6496 * Delete adjacent track points with the same timestamp
6497 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6499 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6501 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6503 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6504 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6506 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6511 gulong removed = vik_track_remove_same_time_points ( trk );
6513 // Track has been updated so update tps:
6514 trw_layer_cancel_tps_of_track ( vtl, trk );
6516 // Inform user how much was deleted as it's not obvious from the normal view
6518 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6519 g_snprintf(str, 64, tmp_str, removed);
6520 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6522 vik_layer_emit_update ( VIK_LAYER(vtl) );
6528 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6530 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6532 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6533 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6535 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6540 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6542 vik_layer_emit_update ( VIK_LAYER(vtl) );
6545 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6547 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6549 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6550 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6552 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6557 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6559 vik_layer_emit_update ( VIK_LAYER(vtl) );
6565 static void trw_layer_reverse ( menu_array_sublayer values )
6567 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6569 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6570 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6572 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6577 vik_track_reverse ( track );
6579 vik_layer_emit_update ( VIK_LAYER(vtl) );
6583 * Open a diary at the specified date
6585 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6588 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6589 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6590 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6591 g_error_free ( err );
6597 * Open a diary at the date of the track or waypoint
6599 static void trw_layer_diary ( menu_array_sublayer values )
6601 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6603 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6604 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6610 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6611 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6612 trw_layer_diary_open ( vtl, date_buf );
6615 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6617 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6618 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6624 if ( wpt->has_timestamp ) {
6625 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6626 trw_layer_diary_open ( vtl, date_buf );
6629 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6634 * Similar to trw_layer_enum_item, but this uses a sorted method
6637 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6639 GList **list = (GList**)udata;
6640 // *list = g_list_prepend(*all, key); //unsorted method
6641 // Sort named list alphabetically
6642 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6647 * Now Waypoint specific sort
6649 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6651 GList **list = (GList**)udata;
6652 // Sort named list alphabetically
6653 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6657 * Track specific sort
6659 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6661 GList **list = (GList**)udata;
6662 // Sort named list alphabetically
6663 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6668 gboolean has_same_track_name;
6669 const gchar *same_track_name;
6670 } same_track_name_udata;
6672 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6674 const gchar* namea = (const gchar*) aa;
6675 const gchar* nameb = (const gchar*) bb;
6678 gint result = strcmp ( namea, nameb );
6680 if ( result == 0 ) {
6681 // Found two names the same
6682 same_track_name_udata *user_data = udata;
6683 user_data->has_same_track_name = TRUE;
6684 user_data->same_track_name = namea;
6687 // Leave ordering the same
6692 * Find out if any tracks have the same name in this hash table
6694 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6696 // Sort items by name, then compare if any next to each other are the same
6698 GList *track_names = NULL;
6699 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6702 if ( ! track_names )
6705 same_track_name_udata udata;
6706 udata.has_same_track_name = FALSE;
6708 // Use sort routine to traverse list comparing items
6709 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6710 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6711 // Still no tracks...
6715 return udata.has_same_track_name;
6719 * Force unqiue track names for the track table specified
6720 * Note the panel is a required parameter to enable the update of the names displayed
6721 * Specify if on tracks or else on routes
6723 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6725 // . Search list for an instance of repeated name
6726 // . get track of this name
6727 // . create new name
6728 // . rename track & update equiv. treeview iter
6729 // . repeat until all different
6731 same_track_name_udata udata;
6733 GList *track_names = NULL;
6734 udata.has_same_track_name = FALSE;
6735 udata.same_track_name = NULL;
6737 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6740 if ( ! track_names )
6743 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6745 // Still no tracks...
6746 if ( ! dummy_list1 )
6749 while ( udata.has_same_track_name ) {
6751 // Find a track with the same name
6754 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6756 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6760 g_critical("Houston, we've had a problem.");
6761 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6762 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6767 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6768 vik_track_set_name ( trk, newname );
6774 // Need want key of it for treeview update
6775 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6777 if ( trkf && udataU.uuid ) {
6781 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6783 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6786 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6788 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6790 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6794 // Start trying to find same names again...
6796 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6797 udata.has_same_track_name = FALSE;
6798 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6800 // No tracks any more - give up searching
6801 if ( ! dummy_list2 )
6802 udata.has_same_track_name = FALSE;
6806 vik_layers_panel_emit_update ( vlp );
6809 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6811 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6814 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6815 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6816 iter = &(vtl->tracks_iter);
6817 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6819 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6820 iter = &(vtl->routes_iter);
6821 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6823 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6824 iter = &(vtl->waypoints_iter);
6825 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6829 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6832 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6834 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6837 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6838 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6839 iter = &(vtl->tracks_iter);
6840 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6842 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6843 iter = &(vtl->routes_iter);
6844 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6846 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6847 iter = &(vtl->waypoints_iter);
6848 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6852 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6858 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6860 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6863 // Ensure list of track names offered is unique
6864 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6865 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6866 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6867 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6873 // Sort list alphabetically for better presentation
6874 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6877 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6881 // Get list of items to delete from the user
6882 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6885 _("Delete Selection"),
6886 _("Select tracks to delete"));
6889 // Delete requested tracks
6890 // since specificly requested, IMHO no need for extra confirmation
6891 if ( delete_list ) {
6893 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6894 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6895 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6897 g_list_free(delete_list);
6898 vik_layer_emit_update( VIK_LAYER(vtl) );
6905 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6907 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6910 // Ensure list of track names offered is unique
6911 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6912 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6913 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6914 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6920 // Sort list alphabetically for better presentation
6921 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6924 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6928 // Get list of items to delete from the user
6929 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6932 _("Delete Selection"),
6933 _("Select routes to delete") );
6936 // Delete requested routes
6937 // since specificly requested, IMHO no need for extra confirmation
6938 if ( delete_list ) {
6940 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6941 // This deletes first route it finds of that name (but uniqueness is enforced above)
6942 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6944 g_list_free(delete_list);
6945 vik_layer_emit_update( VIK_LAYER(vtl) );
6950 gboolean has_same_waypoint_name;
6951 const gchar *same_waypoint_name;
6952 } same_waypoint_name_udata;
6954 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6956 const gchar* namea = (const gchar*) aa;
6957 const gchar* nameb = (const gchar*) bb;
6960 gint result = strcmp ( namea, nameb );
6962 if ( result == 0 ) {
6963 // Found two names the same
6964 same_waypoint_name_udata *user_data = udata;
6965 user_data->has_same_waypoint_name = TRUE;
6966 user_data->same_waypoint_name = namea;
6969 // Leave ordering the same
6974 * Find out if any waypoints have the same name in this layer
6976 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6978 // Sort items by name, then compare if any next to each other are the same
6980 GList *waypoint_names = NULL;
6981 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6984 if ( ! waypoint_names )
6987 same_waypoint_name_udata udata;
6988 udata.has_same_waypoint_name = FALSE;
6990 // Use sort routine to traverse list comparing items
6991 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6992 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6993 // Still no waypoints...
6997 return udata.has_same_waypoint_name;
7001 * Force unqiue waypoint names for this layer
7002 * Note the panel is a required parameter to enable the update of the names displayed
7004 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7006 // . Search list for an instance of repeated name
7007 // . get waypoint of this name
7008 // . create new name
7009 // . rename waypoint & update equiv. treeview iter
7010 // . repeat until all different
7012 same_waypoint_name_udata udata;
7014 GList *waypoint_names = NULL;
7015 udata.has_same_waypoint_name = FALSE;
7016 udata.same_waypoint_name = NULL;
7018 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7021 if ( ! waypoint_names )
7024 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7026 // Still no waypoints...
7027 if ( ! dummy_list1 )
7030 while ( udata.has_same_waypoint_name ) {
7032 // Find a waypoint with the same name
7033 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7037 g_critical("Houston, we've had a problem.");
7038 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7039 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7044 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7046 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7048 // Start trying to find same names again...
7049 waypoint_names = NULL;
7050 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7051 udata.has_same_waypoint_name = FALSE;
7052 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7054 // No waypoints any more - give up searching
7055 if ( ! dummy_list2 )
7056 udata.has_same_waypoint_name = FALSE;
7060 vik_layers_panel_emit_update ( vlp );
7066 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7068 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7071 // Ensure list of waypoint names offered is unique
7072 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7073 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7074 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7075 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7081 // Sort list alphabetically for better presentation
7082 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7084 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7088 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7090 // Get list of items to delete from the user
7091 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7094 _("Delete Selection"),
7095 _("Select waypoints to delete"));
7098 // Delete requested waypoints
7099 // since specificly requested, IMHO no need for extra confirmation
7100 if ( delete_list ) {
7102 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7103 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7104 trw_layer_delete_waypoint_by_name (vtl, l->data);
7106 g_list_free(delete_list);
7108 trw_layer_calculate_bounds_waypoints ( vtl );
7109 vik_layer_emit_update( VIK_LAYER(vtl) );
7117 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7119 vik_treeview_item_toggle_visible ( vt, it );
7125 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7127 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7133 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7135 wp->visible = GPOINTER_TO_INT (on_off);
7141 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7143 wp->visible = !wp->visible;
7149 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7151 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7152 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7153 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7154 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7156 vik_layer_emit_update ( VIK_LAYER(vtl) );
7162 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7164 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7165 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7166 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7167 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7169 vik_layer_emit_update ( VIK_LAYER(vtl) );
7175 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7177 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7178 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7179 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7181 vik_layer_emit_update ( VIK_LAYER(vtl) );
7187 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7189 trk->visible = GPOINTER_TO_INT (on_off);
7195 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7197 trk->visible = !trk->visible;
7203 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7205 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7206 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7207 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7208 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7210 vik_layer_emit_update ( VIK_LAYER(vtl) );
7216 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7218 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7219 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7220 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7221 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7223 vik_layer_emit_update ( VIK_LAYER(vtl) );
7229 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7231 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7232 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7233 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7235 vik_layer_emit_update ( VIK_LAYER(vtl) );
7241 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7243 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7244 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7245 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7246 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7248 vik_layer_emit_update ( VIK_LAYER(vtl) );
7254 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7256 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7257 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7258 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7259 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7261 vik_layer_emit_update ( VIK_LAYER(vtl) );
7267 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7269 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7270 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7271 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7273 vik_layer_emit_update ( VIK_LAYER(vtl) );
7277 * vik_trw_layer_build_waypoint_list_t:
7279 * Helper function to construct a list of #vik_trw_waypoint_list_t
7281 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7283 GList *waypoints_and_layers = NULL;
7284 // build waypoints_and_layers list
7285 while ( waypoints ) {
7286 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7287 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7289 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7290 waypoints = g_list_next ( waypoints );
7292 return waypoints_and_layers;
7296 * trw_layer_create_waypoint_list:
7298 * Create the latest list of waypoints with the associated layer(s)
7299 * Although this will always be from a single layer here
7301 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7303 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7304 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7306 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7310 * trw_layer_analyse_close:
7312 * Stuff to do on dialog closure
7314 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7316 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7317 gtk_widget_destroy ( dialog );
7318 vtl->tracks_analysis_dialog = NULL;
7322 * vik_trw_layer_build_track_list_t:
7324 * Helper function to construct a list of #vik_trw_track_list_t
7326 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7328 GList *tracks_and_layers = NULL;
7329 // build tracks_and_layers list
7331 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7332 vtdl->trk = VIK_TRACK(tracks->data);
7334 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7335 tracks = g_list_next ( tracks );
7337 return tracks_and_layers;
7341 * trw_layer_create_track_list:
7343 * Create the latest list of tracks with the associated layer(s)
7344 * Although this will always be from a single layer here
7346 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7348 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7349 GList *tracks = NULL;
7350 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7351 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7353 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7355 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7358 static void trw_layer_tracks_stats ( menu_array_layer values )
7360 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7361 // There can only be one!
7362 if ( vtl->tracks_analysis_dialog )
7365 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7366 VIK_LAYER(vtl)->name,
7368 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7369 trw_layer_create_track_list,
7370 trw_layer_analyse_close );
7376 static void trw_layer_routes_stats ( menu_array_layer values )
7378 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7379 // There can only be one!
7380 if ( vtl->tracks_analysis_dialog )
7383 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7384 VIK_LAYER(vtl)->name,
7386 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7387 trw_layer_create_track_list,
7388 trw_layer_analyse_close );
7391 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7393 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7394 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7396 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7399 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7401 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7402 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7405 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7406 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7410 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7412 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7413 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7417 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7418 } else if ( !strncmp(wp->comment, "http", 4) ) {
7419 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7420 } else if ( !strncmp(wp->description, "http", 4) ) {
7421 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7425 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7427 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7429 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7431 // No actual change to the name supplied
7433 if (strcmp(newname, wp->name) == 0 )
7436 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7439 // An existing waypoint has been found with the requested name
7440 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7441 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7446 // Update WP name and refresh the treeview
7447 vik_waypoint_set_name (wp, newname);
7449 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7450 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7452 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7457 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7459 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7461 // No actual change to the name supplied
7463 if (strcmp(newname, trk->name) == 0)
7466 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7469 // An existing track has been found with the requested name
7470 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7471 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7475 // Update track name and refresh GUI parts
7476 vik_track_set_name (trk, newname);
7478 // Update any subwindows that could be displaying this track which has changed name
7479 // Only one Track Edit Window
7480 if ( l->current_tp_track == trk && l->tpwin ) {
7481 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7483 // Property Dialog of the track
7484 vik_trw_layer_propwin_update ( trk );
7486 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7487 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7489 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7494 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7496 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7498 // No actual change to the name supplied
7500 if (strcmp(newname, trk->name) == 0)
7503 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7506 // An existing track has been found with the requested name
7507 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7508 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7512 // Update track name and refresh GUI parts
7513 vik_track_set_name (trk, newname);
7515 // Update any subwindows that could be displaying this track which has changed name
7516 // Only one Track Edit Window
7517 if ( l->current_tp_track == trk && l->tpwin ) {
7518 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7520 // Property Dialog of the track
7521 vik_trw_layer_propwin_update ( trk );
7523 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7524 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7526 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7533 static gboolean is_valid_geocache_name ( gchar *str )
7535 gint len = strlen ( str );
7536 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]));
7539 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7541 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7542 a_acquire_set_filter_track ( trk );
7545 #ifdef VIK_CONFIG_GOOGLE
7546 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7548 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7549 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7552 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7554 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7556 gchar *escaped = uri_escape ( tr->comment );
7557 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7558 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7565 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7566 /* viewpoint is now available instead */
7567 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7569 static menu_array_sublayer pass_along;
7571 gboolean rv = FALSE;
7573 pass_along[MA_VTL] = l;
7574 pass_along[MA_VLP] = vlp;
7575 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7576 pass_along[MA_SUBLAYER_ID] = sublayer;
7577 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7578 pass_along[MA_VVP] = vvp;
7579 pass_along[MA_TV_ITER] = iter;
7580 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7582 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7586 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7588 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7589 gtk_widget_show ( item );
7591 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7592 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7593 if (tr && tr->property_dialog)
7594 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7596 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7597 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7598 if (tr && tr->property_dialog)
7599 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7602 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7604 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7605 gtk_widget_show ( item );
7607 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7608 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7609 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7610 gtk_widget_show ( item );
7612 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7614 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7615 gtk_widget_show ( item );
7617 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7619 // Always create separator as now there is always at least the transform menu option
7620 item = gtk_menu_item_new ();
7621 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7622 gtk_widget_show ( item );
7624 /* could be a right-click using the tool */
7625 if ( vlp != NULL ) {
7626 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7627 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7629 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7630 gtk_widget_show ( item );
7633 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7635 if ( wp && wp->name ) {
7636 if ( is_valid_geocache_name ( wp->name ) ) {
7637 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7638 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7639 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7640 gtk_widget_show ( item );
7642 #ifdef VIK_CONFIG_GEOTAG
7643 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7645 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7646 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7647 gtk_widget_show ( item );
7651 if ( wp && wp->image )
7653 // Set up image paramater
7654 pass_along[MA_MISC] = wp->image;
7656 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7657 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
7658 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7659 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7660 gtk_widget_show ( item );
7662 #ifdef VIK_CONFIG_GEOTAG
7663 GtkWidget *geotag_submenu = gtk_menu_new ();
7664 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7665 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7666 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7667 gtk_widget_show ( item );
7668 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7670 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7672 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7673 gtk_widget_show ( item );
7675 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7676 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7677 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7678 gtk_widget_show ( item );
7685 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7686 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7687 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7688 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7689 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7690 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7691 gtk_widget_show ( item );
7697 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7698 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7700 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7701 gtk_widget_show ( item );
7702 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7703 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7704 gtk_widget_set_sensitive ( item, TRUE );
7706 gtk_widget_set_sensitive ( item, FALSE );
7709 item = gtk_menu_item_new ();
7710 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7711 gtk_widget_show ( item );
7714 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7717 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7719 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7720 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7721 gtk_widget_show ( item );
7724 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7726 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7727 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7729 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7730 gtk_widget_show ( item );
7732 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7733 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7734 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7735 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7736 gtk_widget_show ( item );
7738 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7741 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7742 gtk_widget_show ( item );
7744 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7745 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7746 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7747 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7748 gtk_widget_show ( item );
7750 GtkWidget *vis_submenu = gtk_menu_new ();
7751 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7752 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7753 gtk_widget_show ( item );
7754 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7756 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7757 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7759 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7760 gtk_widget_show ( item );
7762 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7763 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7764 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7765 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7766 gtk_widget_show ( item );
7768 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7769 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7771 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7772 gtk_widget_show ( item );
7774 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7775 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7777 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7780 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7784 if ( l->current_track && !l->current_track->is_route ) {
7785 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7787 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7788 gtk_widget_show ( item );
7790 item = gtk_menu_item_new ();
7791 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7792 gtk_widget_show ( item );
7795 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7796 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7798 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7799 gtk_widget_show ( item );
7801 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7802 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7803 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7804 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7805 gtk_widget_show ( item );
7806 // Make it available only when a new track *not* already in progress
7807 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7809 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7810 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7811 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7812 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7813 gtk_widget_show ( item );
7815 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7816 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7817 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7818 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7819 gtk_widget_show ( item );
7821 GtkWidget *vis_submenu = gtk_menu_new ();
7822 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7823 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7824 gtk_widget_show ( item );
7825 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7827 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7828 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7830 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7831 gtk_widget_show ( item );
7833 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7834 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7836 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7837 gtk_widget_show ( item );
7839 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7840 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7842 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7844 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7845 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7846 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7847 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7848 gtk_widget_show ( item );
7850 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7852 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7853 gtk_widget_show ( item );
7856 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7860 if ( l->current_track && l->current_track->is_route ) {
7861 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7862 // Reuse finish track method
7863 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7864 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7865 gtk_widget_show ( item );
7867 item = gtk_menu_item_new ();
7868 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7869 gtk_widget_show ( item );
7872 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7873 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7874 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7875 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7876 gtk_widget_show ( item );
7878 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7879 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7880 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7881 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7882 gtk_widget_show ( item );
7883 // Make it available only when a new track *not* already in progress
7884 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7886 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7887 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7889 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7890 gtk_widget_show ( item );
7892 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7893 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7894 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7895 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7896 gtk_widget_show ( item );
7898 GtkWidget *vis_submenu = gtk_menu_new ();
7899 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7900 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7901 gtk_widget_show ( item );
7902 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7904 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7905 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7906 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7907 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7908 gtk_widget_show ( item );
7910 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7911 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7912 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7913 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7914 gtk_widget_show ( item );
7916 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7917 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7919 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7921 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7922 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7924 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7926 gtk_widget_show ( item );
7928 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7930 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7931 gtk_widget_show ( item );
7935 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7936 GtkWidget *submenu_sort = gtk_menu_new ();
7937 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7939 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7940 gtk_widget_show ( item );
7941 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7943 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7944 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7947 gtk_widget_show ( item );
7949 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7950 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7952 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7953 gtk_widget_show ( item );
7956 GtkWidget *upload_submenu = gtk_menu_new ();
7958 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7960 item = gtk_menu_item_new ();
7961 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7962 gtk_widget_show ( item );
7964 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7965 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7966 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7967 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7968 if ( l->current_track ) {
7969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7970 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7971 gtk_widget_show ( item );
7974 item = gtk_menu_item_new ();
7975 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7976 gtk_widget_show ( item );
7979 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7980 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7982 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7983 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7985 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7986 gtk_widget_show ( item );
7988 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7990 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7991 gtk_widget_show ( item );
7993 GtkWidget *goto_submenu;
7994 goto_submenu = gtk_menu_new ();
7995 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7996 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7997 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7998 gtk_widget_show ( item );
7999 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8001 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8002 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8004 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8005 gtk_widget_show ( item );
8007 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8010 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8011 gtk_widget_show ( item );
8013 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8016 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8017 gtk_widget_show ( item );
8019 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8020 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8021 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8022 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8023 gtk_widget_show ( item );
8025 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8026 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8028 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8029 gtk_widget_show ( item );
8031 // Routes don't have speeds
8032 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8033 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8034 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8036 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8037 gtk_widget_show ( item );
8040 GtkWidget *combine_submenu;
8041 combine_submenu = gtk_menu_new ();
8042 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8043 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8044 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8045 gtk_widget_show ( item );
8046 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8048 // Routes don't have times or segments...
8049 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8050 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8053 gtk_widget_show ( item );
8055 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8057 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8058 gtk_widget_show ( item );
8061 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8063 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8064 gtk_widget_show ( item );
8066 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8067 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8069 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8071 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8072 gtk_widget_show ( item );
8074 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8075 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8077 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8079 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8080 gtk_widget_show ( item );
8082 GtkWidget *split_submenu;
8083 split_submenu = gtk_menu_new ();
8084 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8086 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8087 gtk_widget_show ( item );
8088 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8090 // Routes don't have times or segments...
8091 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8092 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8094 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8095 gtk_widget_show ( item );
8097 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8098 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8100 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8101 gtk_widget_show ( item );
8104 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8106 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8107 gtk_widget_show ( item );
8109 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8110 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8111 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8112 gtk_widget_show ( item );
8113 // Make it available only when a trackpoint is selected.
8114 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8116 GtkWidget *insert_submenu = gtk_menu_new ();
8117 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8118 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8119 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8120 gtk_widget_show ( item );
8121 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8123 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8125 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8126 gtk_widget_show ( item );
8127 // Make it available only when a point is selected
8128 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8130 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8132 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8133 gtk_widget_show ( item );
8134 // Make it available only when a point is selected
8135 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8137 GtkWidget *delete_submenu;
8138 delete_submenu = gtk_menu_new ();
8139 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8140 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8141 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8142 gtk_widget_show ( item );
8143 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8145 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8146 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8148 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8149 gtk_widget_show ( item );
8150 // Make it available only when a point is selected
8151 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8153 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8155 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8156 gtk_widget_show ( item );
8158 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8160 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8161 gtk_widget_show ( item );
8163 GtkWidget *transform_submenu;
8164 transform_submenu = gtk_menu_new ();
8165 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8167 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8168 gtk_widget_show ( item );
8169 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8171 GtkWidget *dem_submenu;
8172 dem_submenu = gtk_menu_new ();
8173 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8174 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
8175 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8176 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8178 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8180 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8181 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8182 gtk_widget_show ( item );
8184 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8186 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8187 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8188 gtk_widget_show ( item );
8190 GtkWidget *smooth_submenu;
8191 smooth_submenu = gtk_menu_new ();
8192 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8193 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8194 gtk_widget_show ( item );
8195 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8197 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8199 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8200 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8201 gtk_widget_show ( item );
8203 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8205 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8206 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8207 gtk_widget_show ( item );
8209 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8210 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8212 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8215 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8216 gtk_widget_show ( item );
8218 // Routes don't have timestamps - so this is only available for tracks
8219 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8220 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8222 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8223 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8224 gtk_widget_show ( item );
8227 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8228 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8230 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8233 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8234 gtk_widget_show ( item );
8236 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8237 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8238 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8239 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8240 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8241 gtk_widget_show ( item );
8244 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8246 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8247 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8249 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8250 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
8251 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8252 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8253 gtk_widget_show ( item );
8256 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8259 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8260 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8261 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8262 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8263 gtk_widget_show ( item );
8265 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8266 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8268 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8269 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8272 gtk_widget_show ( item );
8274 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8275 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8276 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
8277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8278 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8279 gtk_widget_show ( item );
8282 // ATM can't upload a single waypoint but can do waypoints to a GPS
8283 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8284 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8286 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8287 gtk_widget_show ( item );
8288 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8290 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8291 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8293 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8294 gtk_widget_show ( item );
8298 // Only made available if a suitable program is installed
8299 if ( have_diary_program ) {
8300 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8301 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8302 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8304 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8305 gtk_widget_show ( item );
8309 #ifdef VIK_CONFIG_GOOGLE
8310 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8312 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8313 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8315 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8316 gtk_widget_show ( item );
8320 // Some things aren't usable with routes
8321 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8322 #ifdef VIK_CONFIG_OPENSTREETMAP
8323 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8324 // Convert internal pointer into track
8325 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8326 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8328 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8329 gtk_widget_show ( item );
8332 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8336 gtk_widget_show ( item );
8338 /* ATM This function is only available via the layers panel, due to needing a vlp */
8340 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8341 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8342 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8344 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8345 gtk_widget_show ( item );
8349 #ifdef VIK_CONFIG_GEOTAG
8350 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8352 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8353 gtk_widget_show ( item );
8357 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8358 // Only show on viewport popmenu when a trackpoint is selected
8359 if ( ! vlp && l->current_tpl ) {
8361 item = gtk_menu_item_new ();
8362 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8363 gtk_widget_show ( item );
8365 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8366 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8368 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8369 gtk_widget_show ( item );
8373 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8374 GtkWidget *transform_submenu;
8375 transform_submenu = gtk_menu_new ();
8376 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8377 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8378 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8379 gtk_widget_show ( item );
8380 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8382 GtkWidget *dem_submenu;
8383 dem_submenu = gtk_menu_new ();
8384 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8385 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
8386 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8387 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8389 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8390 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8391 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8392 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8393 gtk_widget_show ( item );
8395 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8397 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8398 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8399 gtk_widget_show ( item );
8402 gtk_widget_show_all ( GTK_WIDGET(menu) );
8407 // TODO: Probably better to rework this track manipulation in viktrack.c
8408 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8411 if (!vtl->current_tpl)
8414 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8415 VikTrackpoint *tp_other = NULL;
8418 if (!vtl->current_tpl->prev)
8420 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8422 if (!vtl->current_tpl->next)
8424 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8427 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8430 VikTrackpoint *tp_new = vik_trackpoint_new();
8431 struct LatLon ll_current, ll_other;
8432 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8433 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8435 /* main positional interpolation */
8436 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8437 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8439 /* Now other properties that can be interpolated */
8440 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8442 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8443 /* Note here the division is applied to each part, then added
8444 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8445 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8446 tp_new->has_timestamp = TRUE;
8449 if (tp_current->speed != NAN && tp_other->speed != NAN)
8450 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8452 /* TODO - improve interpolation of course, as it may not be correct.
8453 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8454 [similar applies if value is in radians] */
8455 if (tp_current->course != NAN && tp_other->course != NAN)
8456 tp_new->course = (tp_current->course + tp_other->course)/2;
8458 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8460 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8461 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8463 // Otherwise try routes
8464 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8468 gint index = g_list_index ( trk->trackpoints, tp_current );
8472 // NB no recalculation of bounds since it is inserted between points
8473 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8478 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8484 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8488 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8490 if ( vtl->current_tpl )
8492 vtl->current_tpl = NULL;
8493 vtl->current_tp_track = NULL;
8494 vtl->current_tp_id = NULL;
8495 vik_layer_emit_update(VIK_LAYER(vtl));
8499 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8501 VikTrack *trk = vtl->current_tp_track;
8503 // Notional center of a track is simply an average of the bounding box extremities
8504 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8505 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er );
8506 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8509 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8511 g_assert ( vtl->tpwin != NULL );
8512 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8513 trw_layer_cancel_current_tp ( vtl, TRUE );
8515 if ( vtl->current_tpl == NULL )
8518 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8520 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8521 my_tpwin_set_tp ( vtl );
8523 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8525 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8527 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8531 trw_layer_trackpoint_selected_delete ( vtl, tr );
8533 if ( vtl->current_tpl )
8534 // Reset dialog with the available adjacent trackpoint
8535 my_tpwin_set_tp ( vtl );
8537 vik_layer_emit_update(VIK_LAYER(vtl));
8539 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8541 if ( vtl->current_tp_track ) {
8542 vtl->current_tpl = vtl->current_tpl->next;
8543 my_tpwin_set_tp ( vtl );
8545 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8547 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8549 if ( vtl->current_tp_track ) {
8550 vtl->current_tpl = vtl->current_tpl->prev;
8551 my_tpwin_set_tp ( vtl );
8553 vik_layer_emit_update(VIK_LAYER(vtl));
8555 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8557 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8558 vik_layer_emit_update(VIK_LAYER(vtl));
8560 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8561 vik_layer_emit_update(VIK_LAYER(vtl));
8565 * trw_layer_dialog_shift:
8566 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8568 * Try to reposition a dialog if it's over the specified coord
8569 * so to not obscure the item of interest
8571 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8573 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8575 // Attempt force dialog to be shown so we can find out where it is more reliably...
8576 while ( gtk_events_pending() )
8577 gtk_main_iteration ();
8579 // get parent window position & size
8580 gint win_pos_x, win_pos_y;
8581 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8583 gint win_size_x, win_size_y;
8584 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8586 // get own dialog size
8587 gint dia_size_x, dia_size_y;
8588 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8590 // get own dialog position
8591 gint dia_pos_x, dia_pos_y;
8592 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8594 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8595 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8597 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8599 gint vp_xx, vp_yy; // In viewport pixels
8600 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8602 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8606 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8608 // Transform Viewport pixels into absolute pixels
8609 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8610 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8612 // Is dialog over the point (to within an ^^ edge value)
8613 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8614 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8618 gint hh = vik_viewport_get_height ( vvp );
8620 // Consider the difference in viewport to the full window
8621 gint offset_y = dest_y;
8622 // Add difference between dialog and window sizes
8623 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8625 if ( vp_yy > hh/2 ) {
8626 // Point in bottom half, move window to top half
8627 gtk_window_move ( dialog, dia_pos_x, offset_y );
8630 // Point in top half, move dialog down
8631 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8635 // Shift left<->right
8636 gint ww = vik_viewport_get_width ( vvp );
8638 // Consider the difference in viewport to the full window
8639 gint offset_x = dest_x;
8640 // Add difference between dialog and window sizes
8641 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8643 if ( vp_xx > ww/2 ) {
8644 // Point on right, move window to left
8645 gtk_window_move ( dialog, offset_x, dia_pos_y );
8648 // Point on left, move right
8649 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8657 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8661 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8662 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8663 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8664 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8666 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8668 if ( vtl->current_tpl ) {
8669 // get tp pixel position
8670 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8672 // Shift up<->down to try not to obscure the trackpoint.
8673 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8677 if ( vtl->current_tpl )
8678 if ( vtl->current_tp_track )
8679 my_tpwin_set_tp ( vtl );
8680 /* set layer name and TP data */
8683 /***************************************************************************
8685 ***************************************************************************/
8687 /*** Utility data structures and functions ****/
8691 gint closest_x, closest_y;
8692 gboolean draw_images;
8693 gpointer *closest_wp_id;
8694 VikWaypoint *closest_wp;
8700 gint closest_x, closest_y;
8701 gpointer closest_track_id;
8702 VikTrackpoint *closest_tp;
8708 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8714 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8716 // If waypoint has an image then use the image size to select
8717 if ( params->draw_images && wp->image ) {
8718 gint slackx, slacky;
8719 slackx = wp->image_width / 2;
8720 slacky = wp->image_height / 2;
8722 if ( x <= params->x + slackx && x >= params->x - slackx
8723 && y <= params->y + slacky && y >= params->y - slacky ) {
8724 params->closest_wp_id = id;
8725 params->closest_wp = wp;
8726 params->closest_x = x;
8727 params->closest_y = y;
8730 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8731 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8732 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8734 params->closest_wp_id = id;
8735 params->closest_wp = wp;
8736 params->closest_x = x;
8737 params->closest_y = y;
8741 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8743 GList *tpl = t->trackpoints;
8749 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8755 tp = VIK_TRACKPOINT(tpl->data);
8757 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8759 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8760 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8761 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8763 params->closest_track_id = id;
8764 params->closest_tp = tp;
8765 params->closest_tpl = tpl;
8766 params->closest_x = x;
8767 params->closest_y = y;
8773 // ATM: Leave this as 'Track' only.
8774 // Not overly bothered about having a snap to route trackpoint capability
8775 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8777 TPSearchParams params;
8781 params.closest_track_id = NULL;
8782 params.closest_tp = NULL;
8783 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8784 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8785 return params.closest_tp;
8788 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8790 WPSearchParams params;
8794 params.draw_images = vtl->drawimages;
8795 params.closest_wp = NULL;
8796 params.closest_wp_id = NULL;
8797 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8798 return params.closest_wp;
8802 // Some forward declarations
8803 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8804 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8805 static void marker_end_move ( tool_ed_t *t );
8808 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8812 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8814 // Here always allow snapping back to the original location
8815 // this is useful when one decides not to move the thing afterall
8816 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8819 if ( event->state & GDK_CONTROL_MASK )
8821 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8823 new_coord = tp->coord;
8827 if ( event->state & GDK_SHIFT_MASK )
8829 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8831 new_coord = wp->coord;
8835 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8837 marker_moveto ( t, x, y );
8844 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8846 if ( t->holding && event->button == 1 )
8849 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8852 if ( event->state & GDK_CONTROL_MASK )
8854 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8856 new_coord = tp->coord;
8860 if ( event->state & GDK_SHIFT_MASK )
8862 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8864 new_coord = wp->coord;
8867 marker_end_move ( t );
8869 // Determine if working on a waypoint or a trackpoint
8870 if ( t->is_waypoint ) {
8871 // Update waypoint position
8872 vtl->current_wp->coord = new_coord;
8873 trw_layer_calculate_bounds_waypoints ( vtl );
8874 // Reset waypoint pointer
8875 vtl->current_wp = NULL;
8876 vtl->current_wp_id = NULL;
8879 if ( vtl->current_tpl ) {
8880 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8882 if ( vtl->current_tp_track )
8883 vik_track_calculate_bounds ( vtl->current_tp_track );
8886 if ( vtl->current_tp_track )
8887 my_tpwin_set_tp ( vtl );
8888 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8892 vik_layer_emit_update ( VIK_LAYER(vtl) );
8899 Returns true if a waypoint or track is found near the requested event position for this particular layer
8900 The item found is automatically selected
8901 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8903 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8905 if ( event->button != 1 )
8908 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8911 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8915 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8917 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8919 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8920 WPSearchParams wp_params;
8921 wp_params.vvp = vvp;
8922 wp_params.x = event->x;
8923 wp_params.y = event->y;
8924 wp_params.draw_images = vtl->drawimages;
8925 wp_params.closest_wp_id = NULL;
8926 wp_params.closest_wp = NULL;
8928 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8930 if ( wp_params.closest_wp ) {
8933 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8935 // Too easy to move it so must be holding shift to start immediately moving it
8936 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8937 if ( event->state & GDK_SHIFT_MASK ||
8938 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8939 // Put into 'move buffer'
8940 // NB vvp & vw already set in tet
8941 tet->vtl = (gpointer)vtl;
8942 tet->is_waypoint = TRUE;
8944 marker_begin_move (tet, event->x, event->y);
8947 vtl->current_wp = wp_params.closest_wp;
8948 vtl->current_wp_id = wp_params.closest_wp_id;
8950 if ( event->type == GDK_2BUTTON_PRESS ) {
8951 if ( vtl->current_wp->image ) {
8952 menu_array_sublayer values;
8953 values[MA_VTL] = vtl;
8954 values[MA_MISC] = vtl->current_wp->image;
8955 trw_layer_show_picture ( values );
8959 vik_layer_emit_update ( VIK_LAYER(vtl) );
8965 // Used for both track and route lists
8966 TPSearchParams tp_params;
8967 tp_params.vvp = vvp;
8968 tp_params.x = event->x;
8969 tp_params.y = event->y;
8970 tp_params.closest_track_id = NULL;
8971 tp_params.closest_tp = NULL;
8972 tp_params.closest_tpl = NULL;
8973 tp_params.bbox = bbox;
8975 if (vtl->tracks_visible) {
8976 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8978 if ( tp_params.closest_tp ) {
8980 // Always select + highlight the track
8981 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8983 tet->is_waypoint = FALSE;
8985 // Select the Trackpoint
8986 // Can move it immediately when control held or it's the previously selected tp
8987 if ( event->state & GDK_CONTROL_MASK ||
8988 vtl->current_tpl == tp_params.closest_tpl ) {
8989 // Put into 'move buffer'
8990 // NB vvp & vw already set in tet
8991 tet->vtl = (gpointer)vtl;
8992 marker_begin_move (tet, event->x, event->y);
8995 vtl->current_tpl = tp_params.closest_tpl;
8996 vtl->current_tp_id = tp_params.closest_track_id;
8997 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8999 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9002 my_tpwin_set_tp ( vtl );
9004 vik_layer_emit_update ( VIK_LAYER(vtl) );
9009 // Try again for routes
9010 if (vtl->routes_visible) {
9011 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9013 if ( tp_params.closest_tp ) {
9015 // Always select + highlight the track
9016 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9018 tet->is_waypoint = FALSE;
9020 // Select the Trackpoint
9021 // Can move it immediately when control held or it's the previously selected tp
9022 if ( event->state & GDK_CONTROL_MASK ||
9023 vtl->current_tpl == tp_params.closest_tpl ) {
9024 // Put into 'move buffer'
9025 // NB vvp & vw already set in tet
9026 tet->vtl = (gpointer)vtl;
9027 marker_begin_move (tet, event->x, event->y);
9030 vtl->current_tpl = tp_params.closest_tpl;
9031 vtl->current_tp_id = tp_params.closest_track_id;
9032 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9034 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9037 my_tpwin_set_tp ( vtl );
9039 vik_layer_emit_update ( VIK_LAYER(vtl) );
9044 /* these aren't the droids you're looking for */
9045 vtl->current_wp = NULL;
9046 vtl->current_wp_id = NULL;
9047 trw_layer_cancel_current_tp ( vtl, FALSE );
9050 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9055 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9057 if ( event->button != 3 )
9060 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9063 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9066 /* Post menu for the currently selected item */
9068 /* See if a track is selected */
9069 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9070 if ( track && track->visible ) {
9072 if ( track->name ) {
9074 if ( vtl->track_right_click_menu )
9075 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9077 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9084 if ( track->is_route )
9085 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9087 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9089 if ( trkf && udataU.uuid ) {
9092 if ( track->is_route )
9093 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9095 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9097 trw_layer_sublayer_add_menu_items ( vtl,
9098 vtl->track_right_click_menu,
9100 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9106 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9112 /* See if a waypoint is selected */
9113 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9114 if ( waypoint && waypoint->visible ) {
9115 if ( waypoint->name ) {
9117 if ( vtl->wp_right_click_menu )
9118 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9120 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9123 udata.wp = waypoint;
9126 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9128 if ( wpf && udata.uuid ) {
9129 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9131 trw_layer_sublayer_add_menu_items ( vtl,
9132 vtl->wp_right_click_menu,
9134 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9139 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9148 /* background drawing hook, to be passed the viewport */
9149 static gboolean tool_sync_done = TRUE;
9151 static gboolean tool_sync(gpointer data)
9153 VikViewport *vvp = data;
9154 gdk_threads_enter();
9155 vik_viewport_sync(vvp);
9156 tool_sync_done = TRUE;
9157 gdk_threads_leave();
9161 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9164 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9165 gdk_gc_set_function ( t->gc, GDK_INVERT );
9166 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9167 vik_viewport_sync(t->vvp);
9172 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9174 VikViewport *vvp = t->vvp;
9175 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9176 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9180 if (tool_sync_done) {
9181 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9182 tool_sync_done = FALSE;
9186 static void marker_end_move ( tool_ed_t *t )
9188 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9189 g_object_unref ( t->gc );
9193 /*** Edit waypoint ****/
9195 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9197 tool_ed_t *t = g_new(tool_ed_t, 1);
9203 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9208 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9210 WPSearchParams params;
9211 tool_ed_t *t = data;
9212 VikViewport *vvp = t->vvp;
9214 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9221 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9224 if ( vtl->current_wp && vtl->current_wp->visible )
9226 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9228 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9230 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9231 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9233 if ( event->button == 3 )
9234 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9236 marker_begin_move(t, event->x, event->y);
9243 params.x = event->x;
9244 params.y = event->y;
9245 params.draw_images = vtl->drawimages;
9246 params.closest_wp_id = NULL;
9247 params.closest_wp = NULL;
9248 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9249 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9251 if ( event->button == 3 )
9252 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9254 marker_begin_move(t, event->x, event->y);
9257 else if ( params.closest_wp )
9259 if ( event->button == 3 )
9260 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9262 vtl->waypoint_rightclick = FALSE;
9264 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9266 vtl->current_wp = params.closest_wp;
9267 vtl->current_wp_id = params.closest_wp_id;
9269 /* could make it so don't update if old WP is off screen and new is null but oh well */
9270 vik_layer_emit_update ( VIK_LAYER(vtl) );
9274 vtl->current_wp = NULL;
9275 vtl->current_wp_id = NULL;
9276 vtl->waypoint_rightclick = FALSE;
9277 vik_layer_emit_update ( VIK_LAYER(vtl) );
9281 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9283 tool_ed_t *t = data;
9284 VikViewport *vvp = t->vvp;
9286 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9291 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9294 if ( event->state & GDK_CONTROL_MASK )
9296 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9298 new_coord = tp->coord;
9302 if ( event->state & GDK_SHIFT_MASK )
9304 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9305 if ( wp && wp != vtl->current_wp )
9306 new_coord = wp->coord;
9311 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9313 marker_moveto ( t, x, y );
9320 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9322 tool_ed_t *t = data;
9323 VikViewport *vvp = t->vvp;
9325 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9328 if ( t->holding && event->button == 1 )
9331 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9334 if ( event->state & GDK_CONTROL_MASK )
9336 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9338 new_coord = tp->coord;
9342 if ( event->state & GDK_SHIFT_MASK )
9344 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9345 if ( wp && wp != vtl->current_wp )
9346 new_coord = wp->coord;
9349 marker_end_move ( t );
9351 vtl->current_wp->coord = new_coord;
9353 trw_layer_calculate_bounds_waypoints ( vtl );
9354 vik_layer_emit_update ( VIK_LAYER(vtl) );
9357 /* PUT IN RIGHT PLACE!!! */
9358 if ( event->button == 3 && vtl->waypoint_rightclick )
9360 if ( vtl->wp_right_click_menu )
9361 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9362 if ( vtl->current_wp ) {
9363 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9364 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 );
9365 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9367 vtl->waypoint_rightclick = FALSE;
9372 /*** New track ****/
9374 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9381 GdkDrawable *drawable;
9387 * Draw specified pixmap
9389 static gboolean draw_sync ( gpointer data )
9391 draw_sync_t *ds = (draw_sync_t*) data;
9392 // Sometimes don't want to draw
9393 // normally because another update has taken precedent such as panning the display
9394 // which means this pixmap is no longer valid
9395 if ( ds->vtl->draw_sync_do ) {
9396 gdk_threads_enter();
9397 gdk_draw_drawable (ds->drawable,
9400 0, 0, 0, 0, -1, -1);
9401 ds->vtl->draw_sync_done = TRUE;
9402 gdk_threads_leave();
9408 static gchar* distance_string (gdouble distance)
9412 /* draw label with distance */
9413 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9414 switch (dist_units) {
9415 case VIK_UNITS_DISTANCE_MILES:
9416 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9417 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9418 } else if (distance < 1609.4) {
9419 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9421 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9425 // VIK_UNITS_DISTANCE_KILOMETRES
9426 if (distance >= 1000 && distance < 100000) {
9427 g_sprintf(str, "%3.2f km", distance/1000.0);
9428 } else if (distance < 1000) {
9429 g_sprintf(str, "%d m", (int)distance);
9431 g_sprintf(str, "%d km", (int)distance/1000);
9435 return g_strdup (str);
9439 * Actually set the message in statusbar
9441 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9443 // Only show elevation data when track has some elevation properties
9444 gchar str_gain_loss[64];
9445 str_gain_loss[0] = '\0';
9446 gchar str_last_step[64];
9447 str_last_step[0] = '\0';
9448 gchar *str_total = distance_string (distance);
9450 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9451 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9452 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9454 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9457 if ( last_step > 0 ) {
9458 gchar *tmp = distance_string (last_step);
9459 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9463 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9465 // Write with full gain/loss information
9466 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9467 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9469 g_free ( str_total );
9473 * Figure out what information should be set in the statusbar and then write it
9475 static void update_statusbar ( VikTrwLayer *vtl )
9477 // Get elevation data
9478 gdouble elev_gain, elev_loss;
9479 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9481 /* Find out actual distance of current track */
9482 gdouble distance = vik_track_get_length (vtl->current_track);
9484 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9488 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9490 /* if we haven't sync'ed yet, we don't have time to do more. */
9491 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9492 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9494 static GdkPixmap *pixmap = NULL;
9496 // Need to check in case window has been resized
9497 w1 = vik_viewport_get_width(vvp);
9498 h1 = vik_viewport_get_height(vvp);
9500 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9502 gdk_drawable_get_size (pixmap, &w2, &h2);
9503 if (w1 != w2 || h1 != h2) {
9504 g_object_unref ( G_OBJECT ( pixmap ) );
9505 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9508 // Reset to background
9509 gdk_draw_drawable (pixmap,
9510 vtl->current_track_newpoint_gc,
9511 vik_viewport_get_pixmap(vvp),
9512 0, 0, 0, 0, -1, -1);
9514 draw_sync_t *passalong;
9517 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9519 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9520 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9521 // thus when we come to reset to the background it would include what we have already drawn!!
9522 gdk_draw_line ( pixmap,
9523 vtl->current_track_newpoint_gc,
9524 x1, y1, event->x, event->y );
9525 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9527 /* Find out actual distance of current track */
9528 gdouble distance = vik_track_get_length (vtl->current_track);
9530 // Now add distance to where the pointer is //
9533 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9534 vik_coord_to_latlon ( &coord, &ll );
9535 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9536 distance = distance + last_step;
9538 // Get elevation data
9539 gdouble elev_gain, elev_loss;
9540 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9542 // Adjust elevation data (if available) for the current pointer position
9544 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9545 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9546 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9547 // Adjust elevation of last track point
9548 if ( elev_new > last_tpt->altitude )
9550 elev_gain += elev_new - last_tpt->altitude;
9553 elev_loss += last_tpt->altitude - elev_new;
9558 // Display of the distance 'tooltip' during track creation is controlled by a preference
9560 if ( a_vik_get_create_track_tooltip() ) {
9562 gchar *str = distance_string (distance);
9564 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9565 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9566 pango_layout_set_text (pl, str, -1);
9568 pango_layout_get_pixel_size ( pl, &wd, &hd );
9571 // offset from cursor a bit depending on font size
9575 // Create a background block to make the text easier to read over the background map
9576 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9577 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9578 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9580 g_object_unref ( G_OBJECT ( pl ) );
9581 g_object_unref ( G_OBJECT ( background_block_gc ) );
9585 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9586 passalong->vtl = vtl;
9587 passalong->pixmap = pixmap;
9588 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9589 passalong->gc = vtl->current_track_newpoint_gc;
9593 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9595 // Update statusbar with full gain/loss information
9596 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9598 // draw pixmap when we have time to
9599 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9600 vtl->draw_sync_done = FALSE;
9601 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9603 return VIK_LAYER_TOOL_ACK;
9606 // NB vtl->current_track must be valid
9607 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9610 if ( vtl->current_track->trackpoints ) {
9611 // TODO rework this...
9612 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9613 GList *last = g_list_last(vtl->current_track->trackpoints);
9614 g_free ( last->data );
9615 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9617 vik_track_calculate_bounds ( vtl->current_track );
9621 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9623 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9624 // Bin track if only one point as it's not very useful
9625 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9626 if ( vtl->current_track->is_route )
9627 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9629 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9631 vtl->current_track = NULL;
9632 vik_layer_emit_update ( VIK_LAYER(vtl) );
9634 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9635 undo_trackpoint_add ( vtl );
9636 update_statusbar ( vtl );
9637 vik_layer_emit_update ( VIK_LAYER(vtl) );
9644 * Common function to handle trackpoint button requests on either a route or a track
9645 * . enables adding a point via normal click
9646 * . enables removal of last point via right click
9647 * . finishing of the track or route via double clicking
9649 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9653 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9656 if ( event->button == 2 ) {
9657 // As the display is panning, the new track pixmap is now invalid so don't draw it
9658 // otherwise this drawing done results in flickering back to an old image
9659 vtl->draw_sync_do = FALSE;
9663 if ( event->button == 3 )
9665 if ( !vtl->current_track )
9667 undo_trackpoint_add ( vtl );
9668 update_statusbar ( vtl );
9669 vik_layer_emit_update ( VIK_LAYER(vtl) );
9673 if ( event->type == GDK_2BUTTON_PRESS )
9675 /* subtract last (duplicate from double click) tp then end */
9676 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9678 /* undo last, then end */
9679 undo_trackpoint_add ( vtl );
9680 vtl->current_track = NULL;
9682 vik_layer_emit_update ( VIK_LAYER(vtl) );
9686 tp = vik_trackpoint_new();
9687 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9689 /* snap to other TP */
9690 if ( event->state & GDK_CONTROL_MASK )
9692 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9694 tp->coord = other_tp->coord;
9697 tp->newsegment = FALSE;
9698 tp->has_timestamp = FALSE;
9701 if ( vtl->current_track ) {
9702 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9703 /* Auto attempt to get elevation from DEM data (if it's available) */
9704 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9707 vtl->ct_x1 = vtl->ct_x2;
9708 vtl->ct_y1 = vtl->ct_y2;
9709 vtl->ct_x2 = event->x;
9710 vtl->ct_y2 = event->y;
9712 vik_layer_emit_update ( VIK_LAYER(vtl) );
9716 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9718 // if we were running the route finder, cancel it
9719 vtl->route_finder_started = FALSE;
9721 // ----------------------------------------------------- if current is a route - switch to new track
9722 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9724 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9725 if ( a_vik_get_ask_for_create_track_name() ) {
9726 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
9730 new_track_create_common ( vtl, name );
9733 return tool_new_track_or_route_click ( vtl, event, vvp );
9736 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9738 if ( event->button == 2 ) {
9739 // Pan moving ended - enable potential point drawing again
9740 vtl->draw_sync_do = TRUE;
9741 vtl->draw_sync_done = TRUE;
9745 /*** New route ****/
9747 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9752 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9754 // if we were running the route finder, cancel it
9755 vtl->route_finder_started = FALSE;
9757 // -------------------------- if current is a track - switch to new route,
9758 if ( event->button == 1 && ( ! vtl->current_track ||
9759 (vtl->current_track && !vtl->current_track->is_route ) ) )
9761 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9762 if ( a_vik_get_ask_for_create_track_name() ) {
9763 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
9767 new_route_create_common ( vtl, name );
9770 return tool_new_track_or_route_click ( vtl, event, vvp );
9773 /*** New waypoint ****/
9775 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9780 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9783 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9785 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9786 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9787 trw_layer_calculate_bounds_waypoints ( vtl );
9788 vik_layer_emit_update ( VIK_LAYER(vtl) );
9794 /*** Edit trackpoint ****/
9796 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9798 tool_ed_t *t = g_new(tool_ed_t, 1);
9804 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9809 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9811 tool_ed_t *t = data;
9812 VikViewport *vvp = t->vvp;
9813 TPSearchParams params;
9814 /* OUTDATED DOCUMENTATION:
9815 find 5 pixel range on each side. then put these UTM, and a pointer
9816 to the winning track name (and maybe the winning track itself), and a
9817 pointer to the winning trackpoint, inside an array or struct. pass
9818 this along, do a foreach on the tracks which will do a foreach on the
9821 params.x = event->x;
9822 params.y = event->y;
9823 params.closest_track_id = NULL;
9824 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9825 params.closest_tp = NULL;
9826 params.closest_tpl = NULL;
9827 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9829 if ( event->button != 1 )
9832 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9835 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9838 if ( vtl->current_tpl )
9840 /* 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.) */
9841 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9842 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9847 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9849 if ( current_tr->visible &&
9850 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9851 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9852 marker_begin_move ( t, event->x, event->y );
9858 if ( vtl->tracks_visible )
9859 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9861 if ( params.closest_tp )
9863 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9864 vtl->current_tpl = params.closest_tpl;
9865 vtl->current_tp_id = params.closest_track_id;
9866 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9867 trw_layer_tpwin_init ( vtl );
9868 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9869 vik_layer_emit_update ( VIK_LAYER(vtl) );
9873 if ( vtl->routes_visible )
9874 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9876 if ( params.closest_tp )
9878 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9879 vtl->current_tpl = params.closest_tpl;
9880 vtl->current_tp_id = params.closest_track_id;
9881 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9882 trw_layer_tpwin_init ( vtl );
9883 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9884 vik_layer_emit_update ( VIK_LAYER(vtl) );
9888 /* these aren't the droids you're looking for */
9892 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9894 tool_ed_t *t = data;
9895 VikViewport *vvp = t->vvp;
9897 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9903 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9906 if ( event->state & GDK_CONTROL_MASK )
9908 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9909 if ( tp && tp != vtl->current_tpl->data )
9910 new_coord = tp->coord;
9912 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9915 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9916 marker_moveto ( t, x, y );
9924 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9926 tool_ed_t *t = data;
9927 VikViewport *vvp = t->vvp;
9929 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9931 if ( event->button != 1)
9936 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9939 if ( event->state & GDK_CONTROL_MASK )
9941 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9942 if ( tp && tp != vtl->current_tpl->data )
9943 new_coord = tp->coord;
9946 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9947 if ( vtl->current_tp_track )
9948 vik_track_calculate_bounds ( vtl->current_tp_track );
9950 marker_end_move ( t );
9952 /* diff dist is diff from orig */
9954 my_tpwin_set_tp ( vtl );
9956 vik_layer_emit_update ( VIK_LAYER(vtl) );
9963 /*** Extended Route Finder ***/
9965 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9970 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9973 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9976 vik_layer_emit_update ( VIK_LAYER(vtl) );
9978 /* remove last ' to:...' */
9979 if ( vtl->current_track->comment ) {
9980 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9981 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9982 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9983 last_to - vtl->current_track->comment - 1);
9984 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9991 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9994 if ( !vtl ) return FALSE;
9995 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9996 if ( event->button == 3 && vtl->current_track ) {
9997 tool_extended_route_finder_undo ( vtl );
9999 else if ( event->button == 2 ) {
10000 vtl->draw_sync_do = FALSE;
10003 // if we started the track but via undo deleted all the track points, begin again
10004 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10005 return tool_new_track_or_route_click ( vtl, event, vvp );
10007 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10008 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10009 struct LatLon start, end;
10011 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10012 vik_coord_to_latlon ( &(tp_start->coord), &start );
10013 vik_coord_to_latlon ( &(tmp), &end );
10015 vtl->route_finder_started = TRUE;
10016 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10018 // update UI to let user know what's going on
10019 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10020 VikRoutingEngine *engine = vik_routing_default_engine ( );
10022 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10025 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10026 vik_routing_engine_get_label ( engine ),
10027 start.lat, start.lon, end.lat, end.lon );
10028 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10030 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10033 /* Give GTK a change to display the new status bar before querying the web */
10034 while ( gtk_events_pending ( ) )
10035 gtk_main_iteration ( );
10037 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10039 /* Update UI to say we're done */
10040 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10041 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10042 vik_routing_engine_get_label ( engine ),
10043 start.lat, start.lon, end.lat, end.lon )
10044 : g_strdup_printf ( _("Error getting route from %s."),
10045 vik_routing_engine_get_label ( engine ) );
10046 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10049 vik_layer_emit_update ( VIK_LAYER(vtl) );
10051 vtl->current_track = NULL;
10053 // create a new route where we will add the planned route to
10054 gboolean ret = tool_new_route_click( vtl, event, vvp );
10056 vtl->route_finder_started = TRUE;
10063 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10065 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10066 vtl->route_finder_started = FALSE;
10067 vtl->current_track = NULL;
10068 vik_layer_emit_update ( VIK_LAYER(vtl) );
10070 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10071 tool_extended_route_finder_undo ( vtl );
10078 /*** Show picture ****/
10080 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10085 /* Params are: vvp, event, last match found or NULL */
10086 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10088 if ( wp->image && wp->visible )
10090 gint x, y, slackx, slacky;
10091 GdkEventButton *event = (GdkEventButton *) params[1];
10093 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10094 slackx = wp->image_width / 2;
10095 slacky = wp->image_height / 2;
10096 if ( x <= event->x + slackx && x >= event->x - slackx
10097 && y <= event->y + slacky && y >= event->y - slacky )
10099 params[2] = wp->image; /* we've found a match. however continue searching
10100 * since we want to find the last match -- that
10101 * is, the match that was drawn last. */
10106 static void trw_layer_show_picture ( menu_array_sublayer values )
10108 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10110 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10111 #else /* WINDOWS */
10112 GError *err = NULL;
10113 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10114 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10115 g_free ( quoted_file );
10116 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10118 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() );
10119 g_error_free ( err );
10122 #endif /* WINDOWS */
10125 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10127 gpointer params[3] = { vvp, event, NULL };
10128 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10130 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10133 static menu_array_sublayer values;
10134 values[MA_VTL] = vtl;
10135 values[MA_MISC] = params[2];
10136 trw_layer_show_picture ( values );
10137 return TRUE; /* found a match */
10140 return FALSE; /* go through other layers, searching for a match */
10143 /***************************************************************************
10145 ***************************************************************************/
10148 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10150 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10151 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10154 /* Structure for thumbnail creating data used in the background thread */
10156 VikTrwLayer *vtl; // Layer needed for redrawing
10157 GSList *pics; // Image list
10158 } thumbnail_create_thread_data;
10160 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10162 guint total = g_slist_length(tctd->pics), done = 0;
10163 while ( tctd->pics )
10165 a_thumbnails_create ( (gchar *) tctd->pics->data );
10166 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10168 return -1; /* Abort thread */
10170 tctd->pics = tctd->pics->next;
10173 // Redraw to show the thumbnails as they are now created
10174 if ( IS_VIK_LAYER(tctd->vtl) )
10175 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10180 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10182 while ( tctd->pics )
10184 g_free ( tctd->pics->data );
10185 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10190 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10192 if ( ! vtl->has_verified_thumbnails )
10194 GSList *pics = NULL;
10195 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10198 gint len = g_slist_length ( pics );
10199 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10200 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10203 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10205 (vik_thr_func) create_thumbnails_thread,
10207 (vik_thr_free_func) thumbnail_create_thread_free,
10215 static const gchar* my_track_colors ( gint ii )
10217 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10229 // Fast and reliable way of returning a colour
10230 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10233 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10235 GHashTableIter iter;
10236 gpointer key, value;
10240 g_hash_table_iter_init ( &iter, vtl->tracks );
10242 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10244 // Tracks get a random spread of colours if not already assigned
10245 if ( ! VIK_TRACK(value)->has_color ) {
10246 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10247 VIK_TRACK(value)->color = vtl->track_color;
10249 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10251 VIK_TRACK(value)->has_color = TRUE;
10254 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10257 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10263 g_hash_table_iter_init ( &iter, vtl->routes );
10265 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10267 // Routes get an intermix of reds
10268 if ( ! VIK_TRACK(value)->has_color ) {
10270 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10272 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10273 VIK_TRACK(value)->has_color = TRUE;
10276 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10283 * (Re)Calculate the bounds of the waypoints in this layer,
10284 * This should be called whenever waypoints are changed
10286 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10288 struct LatLon topleft = { 0.0, 0.0 };
10289 struct LatLon bottomright = { 0.0, 0.0 };
10292 GHashTableIter iter;
10293 gpointer key, value;
10295 g_hash_table_iter_init ( &iter, vtl->waypoints );
10297 // Set bounds to first point
10298 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10299 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10300 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10303 // Ensure there is another point...
10304 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10306 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10308 // See if this point increases the bounds.
10309 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10311 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10312 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10313 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10314 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10318 vtl->waypoints_bbox.north = topleft.lat;
10319 vtl->waypoints_bbox.east = bottomright.lon;
10320 vtl->waypoints_bbox.south = bottomright.lat;
10321 vtl->waypoints_bbox.west = topleft.lon;
10324 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10326 vik_track_calculate_bounds ( trk );
10329 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10331 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10332 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10335 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10337 if ( ! VIK_LAYER(vtl)->vt )
10340 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10341 if ( g_hash_table_size (vtl->tracks) > 1 )
10342 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10344 if ( g_hash_table_size (vtl->routes) > 1 )
10345 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10347 if ( g_hash_table_size (vtl->waypoints) > 1 )
10348 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10351 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10353 if ( VIK_LAYER(vtl)->realized )
10354 trw_layer_verify_thumbnails ( vtl, vvp );
10355 trw_layer_track_alloc_colors ( vtl );
10357 trw_layer_calculate_bounds_waypoints ( vtl );
10358 trw_layer_calculate_bounds_tracks ( vtl );
10360 // Apply treeview sort after loading all the tracks for this layer
10361 // (rather than sorted insert on each individual track additional)
10362 // and after subsequent changes to the properties as the specified order may have changed.
10363 // since the sorting of a treeview section is now very quick
10364 // NB sorting is also performed after every name change as well to maintain the list order
10365 trw_layer_sort_all ( vtl );
10367 // Setting metadata time if not otherwise set
10368 if ( vtl->metadata ) {
10370 gboolean need_to_set_time = TRUE;
10371 if ( vtl->metadata->timestamp ) {
10372 need_to_set_time = FALSE;
10373 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10374 need_to_set_time = TRUE;
10377 if ( need_to_set_time ) {
10378 // Could rewrite this as a general get first time of a TRW Layer function
10379 GTimeVal timestamp;
10380 timestamp.tv_usec = 0;
10381 gboolean has_timestamp = FALSE;
10384 gl = g_hash_table_get_values ( vtl->tracks );
10385 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10386 gl = g_list_first ( gl );
10388 // Check times of tracks
10390 // Only need to check the first track as they have been sorted by time
10391 VikTrack *trk = (VikTrack*)gl->data;
10392 // Assume trackpoints already sorted by time
10393 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10394 if ( tpt && tpt->has_timestamp ) {
10395 timestamp.tv_sec = tpt->timestamp;
10396 has_timestamp = TRUE;
10398 g_list_free ( gl );
10401 if ( !has_timestamp ) {
10402 // 'Last' resort - current time
10403 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10404 g_get_current_time ( ×tamp );
10406 // Check times of waypoints
10407 gl = g_hash_table_get_values ( vtl->waypoints );
10409 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10410 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10411 if ( wpt->has_timestamp ) {
10412 if ( timestamp.tv_sec > wpt->timestamp ) {
10413 timestamp.tv_sec = wpt->timestamp;
10414 has_timestamp = TRUE;
10418 g_list_free ( gl );
10421 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10426 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10428 return vtl->coord_mode;
10432 * Uniquify the whole layer
10433 * Also requires the layers panel as the names shown there need updating too
10434 * Returns whether the operation was successful or not
10436 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10438 if ( vtl && vlp ) {
10439 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10440 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10441 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10447 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10449 vik_coord_convert ( &(wp->coord), *dest_mode );
10452 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10454 vik_track_convert ( tr, *dest_mode );
10457 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10459 if ( vtl->coord_mode != dest_mode )
10461 vtl->coord_mode = dest_mode;
10462 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10463 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10464 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10468 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10470 vtl->menu_selection = selection;
10473 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10475 return (vtl->menu_selection);
10478 /* ----------- Downloading maps along tracks --------------- */
10480 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10482 /* TODO: calculating based on current size of viewport */
10483 const gdouble w_at_zoom_0_125 = 0.0013;
10484 const gdouble h_at_zoom_0_125 = 0.0011;
10485 gdouble zoom_factor = zoom_level/0.125;
10487 wh->lat = h_at_zoom_0_125 * zoom_factor;
10488 wh->lon = w_at_zoom_0_125 * zoom_factor;
10490 return 0; /* all OK */
10493 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10495 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10496 (dist->lat >= ABS(to->north_south - from->north_south)))
10499 VikCoord *coord = g_malloc(sizeof(VikCoord));
10500 coord->mode = VIK_COORD_LATLON;
10502 if (ABS(gradient) < 1) {
10503 if (from->east_west > to->east_west)
10504 coord->east_west = from->east_west - dist->lon;
10506 coord->east_west = from->east_west + dist->lon;
10507 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10509 if (from->north_south > to->north_south)
10510 coord->north_south = from->north_south - dist->lat;
10512 coord->north_south = from->north_south + dist->lat;
10513 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10519 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10521 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10522 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10524 VikCoord *next = from;
10526 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10528 list = g_list_prepend(list, next);
10534 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10536 typedef struct _Rect {
10541 #define GLRECT(iter) ((Rect *)((iter)->data))
10544 GList *rects_to_download = NULL;
10547 if (get_download_area_width(vvp, zoom_level, &wh))
10550 GList *iter = tr->trackpoints;
10554 gboolean new_map = TRUE;
10555 VikCoord *cur_coord, tl, br;
10558 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10560 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10561 rect = g_malloc(sizeof(Rect));
10564 rect->center = *cur_coord;
10565 rects_to_download = g_list_prepend(rects_to_download, rect);
10570 gboolean found = FALSE;
10571 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10572 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10583 GList *fillins = NULL;
10584 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10585 /* seems that ATM the function get_next_coord works only for LATLON */
10586 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10587 /* fill-ins for far apart points */
10588 GList *cur_rect, *next_rect;
10589 for (cur_rect = rects_to_download;
10590 (next_rect = cur_rect->next) != NULL;
10591 cur_rect = cur_rect->next) {
10592 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10593 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10594 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10598 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10601 GList *fiter = fillins;
10603 cur_coord = (VikCoord *)(fiter->data);
10604 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10605 rect = g_malloc(sizeof(Rect));
10608 rect->center = *cur_coord;
10609 rects_to_download = g_list_prepend(rects_to_download, rect);
10610 fiter = fiter->next;
10614 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10615 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10619 for (iter = fillins; iter; iter = iter->next)
10620 g_free(iter->data);
10621 g_list_free(fillins);
10623 if (rects_to_download) {
10624 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10625 g_free(rect_iter->data);
10626 g_list_free(rects_to_download);
10630 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10634 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10635 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10636 gint selected_zoom, default_zoom;
10638 VikTrwLayer *vtl = values[MA_VTL];
10639 VikLayersPanel *vlp = values[MA_VLP];
10641 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10642 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10644 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10648 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10650 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10651 int num_maps = g_list_length(vmls);
10654 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10658 // Convert from list of vmls to list of names. Allowing the user to select one of them
10659 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10660 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10662 gchar **np = map_names;
10663 VikMapsLayer **lp = map_layers;
10665 for (i = 0; i < num_maps; i++) {
10666 vml = (VikMapsLayer *)(vmls->data);
10668 *np++ = vik_maps_layer_get_map_label(vml);
10671 // Mark end of the array lists
10675 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10676 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10677 if (cur_zoom == zoom_vals[default_zoom])
10680 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10682 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10685 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10688 for (i = 0; i < num_maps; i++)
10689 g_free(map_names[i]);
10691 g_free(map_layers);
10697 /**** lowest waypoint number calculation ***/
10698 static gint highest_wp_number_name_to_number(const gchar *name) {
10699 if ( strlen(name) == 3 ) {
10700 int n = atoi(name);
10701 if ( n < 100 && name[0] != '0' )
10703 if ( n < 10 && name[0] != '0' )
10711 static void highest_wp_number_reset(VikTrwLayer *vtl)
10713 vtl->highest_wp_number = -1;
10716 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10718 /* if is bigger that top, add it */
10719 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10720 if ( new_wp_num > vtl->highest_wp_number )
10721 vtl->highest_wp_number = new_wp_num;
10724 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10726 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10727 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10728 if ( vtl->highest_wp_number == old_wp_num ) {
10730 vtl->highest_wp_number--;
10732 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10733 /* search down until we find something that *does* exist */
10735 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10736 vtl->highest_wp_number--;
10737 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10742 /* get lowest unused number */
10743 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10746 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10748 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10749 return g_strdup(buf);
10753 * trw_layer_create_track_list_both:
10755 * Create the latest list of tracks and routes
10757 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10759 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10760 GList *tracks = NULL;
10761 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10762 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10764 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10767 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10769 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10771 gchar *title = NULL;
10772 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10773 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10775 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10777 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10781 static void trw_layer_track_list_dialog ( menu_array_layer values )
10783 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10785 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10786 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10790 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10792 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10794 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10795 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );