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_propwin.h"
39 #include "viktrwlayer_analysis.h"
40 #include "viktrwlayer_tracklist.h"
41 #include "viktrwlayer_waypointlist.h"
42 #ifdef VIK_CONFIG_GEOTAG
43 #include "viktrwlayer_geotag.h"
44 #include "geotag_exif.h"
46 #include "garminsymbols.h"
47 #include "thumbnails.h"
48 #include "background.h"
54 #include "geonamessearch.h"
55 #ifdef VIK_CONFIG_OPENSTREETMAP
56 #include "osm-traces.h"
59 #include "datasources.h"
60 #include "datasource_gps.h"
61 #include "vikexttool_datasources.h"
65 #include "vikrouting.h"
67 #include "icons/icons.h"
81 #include <gdk/gdkkeysyms.h>
83 #include <glib/gstdio.h>
84 #include <glib/gi18n.h>
86 #define VIK_TRW_LAYER_TRACK_GC 6
87 #define VIK_TRW_LAYER_TRACK_GCS 10
88 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
89 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
90 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
91 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
92 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
93 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
95 #define DRAWMODE_BY_TRACK 0
96 #define DRAWMODE_BY_SPEED 1
97 #define DRAWMODE_ALL_SAME_COLOR 2
98 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
99 // as we are (re)calculating the colour for every point
104 /* this is how it knows when you click if you are clicking close to a trackpoint. */
105 #define TRACKPOINT_SIZE_APPROX 5
106 #define WAYPOINT_SIZE_APPROX 5
108 #define MIN_STOP_LENGTH 15
109 #define MAX_STOP_LENGTH 86400
110 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
111 /* this is multiplied by user-inputted value from 1-100. */
113 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
115 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
117 FS_XX_SMALL = 0, // 'xx-small'
120 FS_MEDIUM, // DEFAULT
127 struct _VikTrwLayer {
130 GHashTable *tracks_iters;
132 GHashTable *routes_iters;
133 GHashTable *waypoints_iters;
134 GHashTable *waypoints;
135 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
136 gboolean tracks_visible, routes_visible, waypoints_visible;
137 LatLonBBox waypoints_bbox;
139 gboolean track_draw_labels;
142 guint8 drawpoints_size;
143 guint8 drawelevation;
144 guint8 elevation_factor;
148 guint8 drawdirections;
149 guint8 drawdirections_size;
150 guint8 line_thickness;
151 guint8 bg_line_thickness;
152 vik_layer_sort_order_t track_sort_order;
155 VikTRWMetadata *metadata;
157 PangoLayout *tracklabellayout;
158 font_size_t track_font_size;
159 gchar *track_fsize_str;
163 gboolean wp_draw_symbols;
164 font_size_t wp_font_size;
166 vik_layer_sort_order_t wp_sort_order;
168 gdouble track_draw_speed_factor;
170 GdkGC *track_1color_gc;
171 GdkColor track_color;
172 GdkGC *current_track_gc;
173 // Separate GC for a track's potential new point as drawn via separate method
174 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
175 GdkGC *current_track_newpoint_gc;
176 GdkGC *track_bg_gc; GdkColor track_bg_color;
177 GdkGC *waypoint_gc; GdkColor waypoint_color;
178 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
179 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
181 GdkFont *waypoint_font;
182 VikTrack *current_track; // ATM shared between new tracks and new routes
183 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
184 gboolean draw_sync_done;
185 gboolean draw_sync_do;
187 VikCoordMode coord_mode;
189 /* wp editing tool */
190 VikWaypoint *current_wp;
191 gpointer current_wp_id;
193 gboolean waypoint_rightclick;
195 /* track editing tool */
197 VikTrack *current_tp_track;
198 gpointer current_tp_id;
199 VikTrwLayerTpwin *tpwin;
201 /* track editing tool -- more specifically, moving tps */
204 /* route finder tool */
205 gboolean route_finder_started;
206 gboolean route_finder_check_added_track;
207 VikTrack *route_finder_added_track;
208 gboolean route_finder_append;
215 guint16 image_cache_size;
217 /* for waypoint text */
218 PangoLayout *wplabellayout;
220 gboolean has_verified_thumbnails;
222 GtkMenu *wp_right_click_menu;
223 GtkMenu *track_right_click_menu;
226 VikStdLayerMenuItem menu_selection;
228 gint highest_wp_number;
231 GtkWidget *tracks_analysis_dialog;
234 /* A caached waypoint image. */
237 gchar *image; /* filename */
240 struct DrawingParams {
245 guint16 width, height;
246 gdouble cc; // Cosine factor in track directions
247 gdouble ss; // Sine factor in track directions
248 const VikCoord *center;
249 gboolean one_zone, lat_lon;
250 gdouble ce1, ce2, cn1, cn2;
255 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
260 MA_SUBTYPE, // OR END for Layer only
269 typedef gpointer menu_array_layer[2];
270 typedef gpointer menu_array_sublayer[MA_LAST];
272 static void trw_layer_delete_item ( menu_array_sublayer values );
273 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
274 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
276 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
277 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
279 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
280 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
282 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
283 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
285 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
286 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
287 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
290 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_center ( menu_array_sublayer values );
292 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
293 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
294 static void trw_layer_merge_with_other ( menu_array_sublayer values );
295 static void trw_layer_append_track ( menu_array_sublayer values );
296 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
297 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
298 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
299 static void trw_layer_split_segments ( menu_array_sublayer values );
300 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
301 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
303 static void trw_layer_reverse ( menu_array_sublayer values );
304 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
305 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
306 static void trw_layer_show_picture ( menu_array_sublayer values );
307 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
309 static void trw_layer_centerize ( menu_array_layer values );
310 static void trw_layer_auto_view ( menu_array_layer values );
311 static void trw_layer_goto_wp ( menu_array_layer values );
312 static void trw_layer_new_wp ( menu_array_layer values );
313 static void trw_layer_new_track ( menu_array_layer values );
314 static void trw_layer_new_route ( menu_array_layer values );
315 static void trw_layer_finish_track ( menu_array_layer values );
316 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
317 static void trw_layer_auto_tracks_view ( menu_array_layer values );
318 static void trw_layer_delete_all_tracks ( menu_array_layer values );
319 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
320 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
321 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
322 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
324 #ifdef VIK_CONFIG_GEOTAG
325 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
326 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
327 static void trw_layer_geotagging_track ( menu_array_sublayer values );
328 static void trw_layer_geotagging ( menu_array_layer values );
330 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
331 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
332 static void trw_layer_acquire_url_cb ( menu_array_layer values );
333 #ifdef VIK_CONFIG_OPENSTREETMAP
334 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
335 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
337 #ifdef VIK_CONFIG_GEOCACHES
338 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
340 #ifdef VIK_CONFIG_GEOTAG
341 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
343 static void trw_layer_acquire_file_cb ( menu_array_layer values );
344 static void trw_layer_gps_upload ( menu_array_layer values );
346 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
347 static void trw_layer_track_list_dialog ( menu_array_layer values );
348 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
350 // Specific route versions:
351 // Most track handling functions can handle operating on the route list
352 // However these ones are easier in separate functions
353 static void trw_layer_auto_routes_view ( menu_array_layer values );
354 static void trw_layer_delete_all_routes ( menu_array_layer values );
355 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
358 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
359 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
360 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
361 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
363 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
364 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
366 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
367 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
368 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
369 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
371 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
372 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
373 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
374 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
375 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
376 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
377 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
378 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
379 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
380 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
381 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
382 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
383 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
384 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
385 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
386 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
387 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
388 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
389 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
390 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
391 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
392 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
393 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
394 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
396 static void cached_pixbuf_free ( CachedPixbuf *cp );
397 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
399 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
400 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
402 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
403 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
405 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
406 static void highest_wp_number_reset(VikTrwLayer *vtl);
407 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
408 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
410 // Note for the following tool GtkRadioActionEntry texts:
411 // the very first text value is an internal name not displayed anywhere
412 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
413 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
414 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
415 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
416 static VikToolInterface trw_layer_tools[] = {
417 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
418 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
419 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
421 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
423 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
424 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
425 (VikToolMouseFunc) tool_new_track_click,
426 (VikToolMouseMoveFunc) tool_new_track_move,
427 (VikToolMouseFunc) tool_new_track_release,
428 (VikToolKeyFunc) tool_new_track_key_press,
429 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
430 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
432 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
433 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
434 (VikToolMouseFunc) tool_new_route_click,
435 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
436 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
437 (VikToolKeyFunc) tool_new_track_key_press, // -/#
438 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
439 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
441 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
442 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
443 (VikToolMouseFunc) tool_extended_route_finder_click,
444 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
445 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
446 (VikToolKeyFunc) tool_extended_route_finder_key_press,
447 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
448 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
450 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
451 (VikToolConstructorFunc) tool_edit_waypoint_create,
452 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
454 (VikToolMouseFunc) tool_edit_waypoint_click,
455 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
456 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
458 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
460 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
461 (VikToolConstructorFunc) tool_edit_trackpoint_create,
462 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
464 (VikToolMouseFunc) tool_edit_trackpoint_click,
465 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
466 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
468 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
470 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
471 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
472 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
474 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
479 TOOL_CREATE_WAYPOINT=0,
484 TOOL_EDIT_TRACKPOINT,
489 /****** PARAMETERS ******/
491 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
492 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
494 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
495 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
497 #define MIN_POINT_SIZE 2
498 #define MAX_POINT_SIZE 10
500 #define MIN_ARROW_SIZE 3
501 #define MAX_ARROW_SIZE 20
503 static VikLayerParamScale params_scales[] = {
504 /* min max step digits */
505 { 1, 10, 1, 0 }, /* line_thickness */
506 { 0, 100, 1, 0 }, /* track draw speed factor */
507 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
508 /* 5 * step == how much to turn */
509 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
510 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
511 { 5, 500, 5, 0 }, // 5: image cache_size - " "
512 { 0, 8, 1, 0 }, // 6: Background line thickness
513 { 1, 64, 1, 0 }, /* wpsize */
514 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
515 { 1, 100, 1, 0 }, // 9: elevation factor
516 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
517 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
520 static gchar* params_font_sizes[] = {
521 N_("Extra Extra Small"),
527 N_("Extra Extra Large"),
530 // Needs to align with vik_layer_sort_order_t
531 static gchar* params_sort_order[] = {
533 N_("Name Ascending"),
534 N_("Name Descending"),
538 static VikLayerParamData black_color_default ( void ) {
539 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
541 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
542 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
543 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
544 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
545 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
546 static VikLayerParamData trackbgcolor_default ( void ) {
547 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
549 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
550 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
551 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
553 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
554 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
555 static VikLayerParamData wptextcolor_default ( void ) {
556 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
558 static VikLayerParamData wpbgcolor_default ( void ) {
559 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
561 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
562 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
564 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
565 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
566 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
568 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
570 static VikLayerParamData string_default ( void )
572 VikLayerParamData data;
577 VikLayerParam trw_layer_params[] = {
578 { 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 },
579 { 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 },
580 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
582 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
583 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
584 { 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 },
585 { 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 },
586 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
587 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
588 { 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 },
589 { 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 },
590 { 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 },
591 { 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 },
592 { 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 },
593 { 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 },
594 { 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 },
595 { 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 },
596 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
597 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 },
598 { 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 },
600 { 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 },
601 { 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 },
602 { 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,
603 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
604 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
606 { 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 },
607 { 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 },
608 { 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 },
609 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
610 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
611 { 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 },
612 { 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 },
613 { 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 },
614 { 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 },
615 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
617 { 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 },
618 { 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 },
619 { 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 },
620 { 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 },
622 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
623 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
625 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
628 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
630 // Sublayer visibilities
678 *** 1) Add to trw_layer_params and enumeration
679 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
682 /****** END PARAMETERS ******/
684 /* Layer Interface function definitions */
685 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
686 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
687 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
688 static void trw_layer_free ( VikTrwLayer *trwlayer );
689 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
690 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
691 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
692 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
693 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
694 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
695 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
696 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
697 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
698 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
699 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
700 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
701 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
702 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
703 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
704 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
705 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
706 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
707 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
708 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
709 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
710 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
711 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
712 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
713 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
714 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
715 /* End Layer Interface function definitions */
717 VikLayerInterface vik_trw_layer_interface = {
724 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
728 params_groups, /* params_groups */
729 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
733 (VikLayerFuncCreate) trw_layer_create,
734 (VikLayerFuncRealize) trw_layer_realize,
735 (VikLayerFuncPostRead) trw_layer_post_read,
736 (VikLayerFuncFree) trw_layer_free,
738 (VikLayerFuncProperties) NULL,
739 (VikLayerFuncDraw) trw_layer_draw,
740 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
742 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
743 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
745 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
746 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
748 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
749 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
750 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
751 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
752 (VikLayerFuncLayerSelected) trw_layer_selected,
754 (VikLayerFuncMarshall) trw_layer_marshall,
755 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
757 (VikLayerFuncSetParam) trw_layer_set_param,
758 (VikLayerFuncGetParam) trw_layer_get_param,
759 (VikLayerFuncChangeParam) trw_layer_change_param,
761 (VikLayerFuncReadFileData) a_gpspoint_read_file,
762 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
764 (VikLayerFuncDeleteItem) trw_layer_del_item,
765 (VikLayerFuncCutItem) trw_layer_cut_item,
766 (VikLayerFuncCopyItem) trw_layer_copy_item,
767 (VikLayerFuncPasteItem) trw_layer_paste_item,
768 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
770 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
772 (VikLayerFuncSelectClick) trw_layer_select_click,
773 (VikLayerFuncSelectMove) trw_layer_select_move,
774 (VikLayerFuncSelectRelease) trw_layer_select_release,
775 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
778 static gboolean have_diary_program = FALSE;
779 static gboolean have_geojson_export = FALSE;
782 // NB Only performed once per program run
783 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
785 if ( g_find_program_in_path( "rednotebook" ) ) {
786 gchar *stdout = NULL;
787 gchar *stderr = NULL;
788 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
789 if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
790 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
792 g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
794 g_warning ("Diary: stderr: %s", stderr );
796 gchar **tokens = NULL;
797 if ( stdout && g_strcmp0(stdout, "") )
798 tokens = g_strsplit(stdout, " ", 0);
800 tokens = g_strsplit(stderr, " ", 0);
803 gchar *token = tokens[num];
804 while ( token && num < 2 ) {
806 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
807 have_diary_program = TRUE;
812 g_strfreev ( tokens );
818 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
819 have_geojson_export = TRUE;
823 GType vik_trw_layer_get_type ()
825 static GType vtl_type = 0;
829 static const GTypeInfo vtl_info =
831 sizeof (VikTrwLayerClass),
832 NULL, /* base_init */
833 NULL, /* base_finalize */
834 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
835 NULL, /* class_finalize */
836 NULL, /* class_data */
837 sizeof (VikTrwLayer),
839 NULL /* instance init */
841 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
846 VikTRWMetadata *vik_trw_metadata_new()
848 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
851 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
856 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
858 return vtl->metadata;
861 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
864 vik_trw_metadata_free ( vtl->metadata );
865 vtl->metadata = metadata;
870 const gchar *date_str;
872 const VikWaypoint *wpt;
877 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
881 // Might be an easier way to compare dates rather than converting the strings all the time...
882 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
883 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
885 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
894 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
898 // Might be an easier way to compare dates rather than converting the strings all the time...
899 if ( wpt->has_timestamp ) {
900 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
902 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
912 * Find an item by date
914 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
918 df.date_str = date_str;
923 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
925 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
927 if ( select && df.found ) {
928 if ( do_tracks && df.trk ) {
929 struct LatLon maxmin[2] = { {0,0}, {0,0} };
930 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
931 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
932 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
935 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
936 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
938 vik_layer_emit_update ( VIK_LAYER(vtl) );
943 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
945 static menu_array_sublayer values;
951 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
954 values[MA_VTL] = vtl;
955 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
956 values[MA_SUBLAYER_ID] = sublayer;
957 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
959 trw_layer_delete_item ( values );
962 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
964 static menu_array_sublayer values;
970 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
973 values[MA_VTL] = vtl;
974 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
975 values[MA_SUBLAYER_ID] = sublayer;
976 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
978 trw_layer_copy_item_cb(values);
979 trw_layer_cut_item_cb(values);
982 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
984 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
985 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
986 gpointer * sublayer = values[MA_SUBLAYER_ID];
990 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
994 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
995 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
996 if ( wp && wp->name )
999 name = NULL; // Broken :(
1001 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1002 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1003 if ( trk && trk->name )
1006 name = NULL; // Broken :(
1009 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1010 if ( trk && trk->name )
1013 name = NULL; // Broken :(
1016 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1017 subtype, len, name, data);
1021 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1023 trw_layer_copy_item_cb(values);
1024 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1025 trw_layer_delete_item(values);
1028 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1030 // Slightly cheating method, routing via the panels capability
1031 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1034 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1044 GByteArray *ba = g_byte_array_new ();
1046 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1047 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1048 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1049 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1051 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1054 g_byte_array_append ( ba, id, il );
1062 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1069 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1073 w = vik_waypoint_unmarshall ( item, len );
1074 // When copying - we'll create a new name based on the original
1075 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1076 vik_trw_layer_add_waypoint ( vtl, name, w );
1077 waypoint_convert (NULL, w, &vtl->coord_mode);
1080 trw_layer_calculate_bounds_waypoints ( vtl );
1082 // Consider if redraw necessary for the new item
1083 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1084 vik_layer_emit_update ( VIK_LAYER(vtl) );
1087 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1091 t = vik_track_unmarshall ( item, len );
1092 // When copying - we'll create a new name based on the original
1093 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1094 vik_trw_layer_add_track ( vtl, name, t );
1095 vik_track_convert (t, vtl->coord_mode);
1098 // Consider if redraw necessary for the new item
1099 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1100 vik_layer_emit_update ( VIK_LAYER(vtl) );
1103 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1107 t = vik_track_unmarshall ( item, len );
1108 // When copying - we'll create a new name based on the original
1109 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1110 vik_trw_layer_add_route ( vtl, name, t );
1111 vik_track_convert (t, vtl->coord_mode);
1114 // Consider if redraw necessary for the new item
1115 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1116 vik_layer_emit_update ( VIK_LAYER(vtl) );
1122 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1129 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1133 case PARAM_TV: vtl->tracks_visible = data.b; break;
1134 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1135 case PARAM_RV: vtl->routes_visible = data.b; break;
1136 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1137 case PARAM_TLFONTSIZE:
1138 if ( data.u < FS_NUM_SIZES ) {
1139 vtl->track_font_size = data.u;
1140 g_free ( vtl->track_fsize_str );
1141 switch ( vtl->track_font_size ) {
1142 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1143 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1144 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1145 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1146 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1147 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1148 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1152 case PARAM_DM: vtl->drawmode = data.u; break;
1154 vtl->track_color = data.c;
1155 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1157 case PARAM_DP: vtl->drawpoints = data.b; break;
1159 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1160 vtl->drawpoints_size = data.u;
1162 case PARAM_DE: vtl->drawelevation = data.b; break;
1163 case PARAM_DS: vtl->drawstops = data.b; break;
1164 case PARAM_DL: vtl->drawlines = data.b; break;
1165 case PARAM_DD: vtl->drawdirections = data.b; break;
1167 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1168 vtl->drawdirections_size = data.u;
1170 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1171 vtl->stop_length = data.u;
1173 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1174 vtl->elevation_factor = data.u;
1176 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1178 vtl->line_thickness = data.u;
1179 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1182 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1184 vtl->bg_line_thickness = data.u;
1185 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1189 vtl->track_bg_color = data.c;
1190 if ( vtl->track_bg_gc )
1191 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1193 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1194 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1195 case PARAM_DLA: vtl->drawlabels = data.b; break;
1196 case PARAM_DI: vtl->drawimages = data.b; break;
1197 case PARAM_IS: if ( data.u != vtl->image_size )
1199 vtl->image_size = data.u;
1200 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1201 g_queue_free ( vtl->image_cache );
1202 vtl->image_cache = g_queue_new ();
1205 case PARAM_IA: vtl->image_alpha = data.u; break;
1206 case PARAM_ICS: vtl->image_cache_size = data.u;
1207 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1208 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1211 vtl->waypoint_color = data.c;
1212 if ( vtl->waypoint_gc )
1213 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1216 vtl->waypoint_text_color = data.c;
1217 if ( vtl->waypoint_text_gc )
1218 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1221 vtl->waypoint_bg_color = data.c;
1222 if ( vtl->waypoint_bg_gc )
1223 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1226 vtl->wpbgand = data.b;
1227 if ( vtl->waypoint_bg_gc )
1228 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1230 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1231 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1232 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1233 case PARAM_WPFONTSIZE:
1234 if ( data.u < FS_NUM_SIZES ) {
1235 vtl->wp_font_size = data.u;
1236 g_free ( vtl->wp_fsize_str );
1237 switch ( vtl->wp_font_size ) {
1238 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1239 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1240 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1241 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1242 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1243 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1244 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1248 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1250 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1251 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1252 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1253 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1259 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1261 VikLayerParamData rv;
1264 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1265 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1266 case PARAM_RV: rv.b = vtl->routes_visible; break;
1267 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1268 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1269 case PARAM_DM: rv.u = vtl->drawmode; break;
1270 case PARAM_TC: rv.c = vtl->track_color; break;
1271 case PARAM_DP: rv.b = vtl->drawpoints; break;
1272 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1273 case PARAM_DE: rv.b = vtl->drawelevation; break;
1274 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1275 case PARAM_DS: rv.b = vtl->drawstops; break;
1276 case PARAM_SL: rv.u = vtl->stop_length; break;
1277 case PARAM_DL: rv.b = vtl->drawlines; break;
1278 case PARAM_DD: rv.b = vtl->drawdirections; break;
1279 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1280 case PARAM_LT: rv.u = vtl->line_thickness; break;
1281 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1282 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1283 case PARAM_DI: rv.b = vtl->drawimages; break;
1284 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1285 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1286 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1287 case PARAM_IS: rv.u = vtl->image_size; break;
1288 case PARAM_IA: rv.u = vtl->image_alpha; break;
1289 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1290 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1291 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1292 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1293 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1294 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1295 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1296 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1297 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1298 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1300 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1301 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1302 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1303 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1309 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1311 // This '-3' is to account for the first few parameters not in the properties
1312 const gint OFFSET = -3;
1314 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1315 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1318 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1319 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1320 GtkWidget **ww2 = values[UI_CHG_LABELS];
1321 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1322 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1323 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1324 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1325 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1326 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1327 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1328 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1329 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1330 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1331 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1332 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1335 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1338 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1339 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1340 GtkWidget **ww2 = values[UI_CHG_LABELS];
1341 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1342 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1343 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1344 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1345 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1346 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1347 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1348 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1349 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1350 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1351 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1352 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1353 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1354 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1355 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1356 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1359 // Alter sensitivity of all track colours according to the draw track mode.
1362 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1363 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1364 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1365 GtkWidget **ww2 = values[UI_CHG_LABELS];
1366 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1367 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1368 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1369 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1372 case PARAM_MDTIME: {
1373 // Force metadata->timestamp to be always read-only for now.
1374 GtkWidget **ww = values[UI_CHG_WIDGETS];
1375 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1376 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1378 // NB Since other track settings have been split across tabs,
1379 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1384 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1391 // Use byte arrays to store sublayer data
1392 // much like done elsewhere e.g. vik_layer_marshall_params()
1393 GByteArray *ba = g_byte_array_new ( );
1398 guint object_length;
1401 // the length of the item
1402 // the sublayer type of item
1403 // the the actual item
1404 #define tlm_append(object_pointer, size, type) \
1406 object_length = (size); \
1407 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1408 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1409 g_byte_array_append ( ba, (object_pointer), object_length );
1411 // Layer parameters first
1412 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1413 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1414 g_byte_array_append ( ba, pd, pl );
1417 // Now sublayer data
1418 GHashTableIter iter;
1419 gpointer key, value;
1422 g_hash_table_iter_init ( &iter, vtl->waypoints );
1423 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1424 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1425 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1430 g_hash_table_iter_init ( &iter, vtl->tracks );
1431 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1432 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1433 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1438 g_hash_table_iter_init ( &iter, vtl->routes );
1439 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1440 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1441 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1451 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1453 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1455 gint consumed_length;
1457 // First the overall layer parameters
1458 memcpy(&pl, data, sizeof(pl));
1460 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1463 consumed_length = pl;
1464 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1466 #define tlm_size (*(gint *)data)
1467 // See marshalling above for order of how this is written
1469 data += sizeof_len_and_subtype + tlm_size;
1471 // Now the individual sublayers:
1473 while ( *data && consumed_length < len ) {
1474 // Normally four extra bytes at the end of the datastream
1475 // (since it's a GByteArray and that's where it's length is stored)
1476 // So only attempt read when there's an actual block of sublayer data
1477 if ( consumed_length + tlm_size < len ) {
1479 // Reuse pl to read the subtype from the data stream
1480 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1482 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1483 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1484 gchar *name = g_strdup ( trk->name );
1485 vik_trw_layer_add_track ( vtl, name, trk );
1488 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1489 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1490 gchar *name = g_strdup ( wp->name );
1491 vik_trw_layer_add_waypoint ( vtl, name, wp );
1494 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1495 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1496 gchar *name = g_strdup ( trk->name );
1497 vik_trw_layer_add_route ( vtl, name, trk );
1501 consumed_length += tlm_size + sizeof_len_and_subtype;
1504 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1506 // Not stored anywhere else so need to regenerate
1507 trw_layer_calculate_bounds_waypoints ( vtl );
1512 // Keep interesting hash function at least visible
1514 static guint strcase_hash(gconstpointer v)
1516 // 31 bit hash function
1519 gchar s[128]; // malloc is too slow for reading big files
1522 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1523 p[i] = toupper(t[i]);
1529 for (p += 1; *p != '\0'; p++)
1530 h = (h << 5) - h + *p;
1537 // Stick a 1 at the end of the function name to make it more unique
1538 // thus more easily searchable in a simple text editor
1539 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1541 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1542 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1544 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1545 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1547 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1548 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1549 // and with normal PC processing capabilities - it has negligibile performance impact
1550 // This also minimized the amount of rework - as the management of the hash tables already exists.
1552 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1553 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1554 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1556 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1557 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1558 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1559 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1560 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1561 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1563 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1565 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1567 // Param settings that are not available via the GUI
1568 // Force to on after processing params (which defaults them to off with a zero value)
1569 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1571 rv->metadata = vik_trw_metadata_new ();
1572 rv->draw_sync_done = TRUE;
1573 rv->draw_sync_do = TRUE;
1574 // Everything else is 0, FALSE or NULL
1580 static void trw_layer_free ( VikTrwLayer *trwlayer )
1582 g_hash_table_destroy(trwlayer->waypoints);
1583 g_hash_table_destroy(trwlayer->waypoints_iters);
1584 g_hash_table_destroy(trwlayer->tracks);
1585 g_hash_table_destroy(trwlayer->tracks_iters);
1586 g_hash_table_destroy(trwlayer->routes);
1587 g_hash_table_destroy(trwlayer->routes_iters);
1589 /* ODC: replace with GArray */
1590 trw_layer_free_track_gcs ( trwlayer );
1592 if ( trwlayer->wp_right_click_menu )
1593 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1595 if ( trwlayer->track_right_click_menu )
1596 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1598 if ( trwlayer->tracklabellayout != NULL)
1599 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1601 if ( trwlayer->wplabellayout != NULL)
1602 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1604 if ( trwlayer->waypoint_gc != NULL )
1605 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1607 if ( trwlayer->waypoint_text_gc != NULL )
1608 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1610 if ( trwlayer->waypoint_bg_gc != NULL )
1611 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1613 g_free ( trwlayer->wp_fsize_str );
1614 g_free ( trwlayer->track_fsize_str );
1616 if ( trwlayer->tpwin != NULL )
1617 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1619 if ( trwlayer->tracks_analysis_dialog != NULL )
1620 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1622 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1623 g_queue_free ( trwlayer->image_cache );
1626 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1630 dp->highlight = highlight;
1631 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1632 dp->xmpp = vik_viewport_get_xmpp ( vp );
1633 dp->ympp = vik_viewport_get_ympp ( vp );
1634 dp->width = vik_viewport_get_width ( vp );
1635 dp->height = vik_viewport_get_height ( vp );
1636 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1637 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1639 dp->center = vik_viewport_get_center ( vp );
1640 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1641 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1646 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1647 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1648 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1650 dp->ce1 = dp->center->east_west-w2;
1651 dp->ce2 = dp->center->east_west+w2;
1652 dp->cn1 = dp->center->north_south-h2;
1653 dp->cn2 = dp->center->north_south+h2;
1654 } else if ( dp->lat_lon ) {
1655 VikCoord upperleft, bottomright;
1656 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1657 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1658 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1659 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1660 dp->ce1 = upperleft.east_west;
1661 dp->ce2 = bottomright.east_west;
1662 dp->cn1 = bottomright.north_south;
1663 dp->cn2 = upperleft.north_south;
1666 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1670 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1671 * Here a simple traffic like light colour system is used:
1672 * . slow points are red
1673 * . average is yellow
1674 * . fast points are green
1676 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1679 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1680 if ( average_speed > 0 ) {
1681 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1682 if ( rv < low_speed )
1683 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1684 else if ( rv > high_speed )
1685 return VIK_TRW_LAYER_TRACK_GC_FAST;
1687 return VIK_TRW_LAYER_TRACK_GC_AVER;
1690 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1693 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1695 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1696 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1697 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1698 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1702 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1704 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1706 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1707 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1709 // Fallback if parse failure
1710 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1712 g_free ( label_markup );
1714 gint label_x, label_y;
1716 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1718 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1719 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1723 * distance_in_preferred_units:
1724 * @dist: The source distance in standard SI Units (i.e. metres)
1726 * TODO: This is a generic function that could be moved into globals.c or utils.c
1728 * Probably best used if you have a only few conversions to perform.
1729 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1730 * since it will be doing the preference check on each call
1732 * Returns: The distance in the units as specified by the preferences
1734 static gdouble distance_in_preferred_units ( gdouble dist )
1737 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1738 switch (dist_units) {
1739 case VIK_UNITS_DISTANCE_MILES:
1740 mydist = VIK_METERS_TO_MILES(dist);
1742 // VIK_UNITS_DISTANCE_KILOMETRES:
1744 mydist = dist/1000.0;
1751 * trw_layer_draw_dist_labels:
1753 * Draw a few labels along a track at nicely seperated distances
1754 * This might slow things down if there's many tracks being displayed with this on.
1756 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1758 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1759 25.0, 40.0, 50.0, 75.0, 100.0,
1760 150.0, 200.0, 250.0, 500.0, 1000.0};
1762 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1764 // Convert to specified unit to find the friendly breakdown value
1765 dist = distance_in_preferred_units ( dist );
1769 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1770 if ( chunksd[i] > dist ) {
1772 dist = chunksd[index];
1777 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1779 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1780 gdouble dist_i = dist * i;
1782 // Convert distance back into metres for use in finding a trackpoint
1783 switch (dist_units) {
1784 case VIK_UNITS_DISTANCE_MILES:
1785 dist_i = VIK_MILES_TO_METERS(dist_i);
1787 // VIK_UNITS_DISTANCE_KILOMETRES:
1789 dist_i = dist_i*1000.0;
1793 gdouble dist_current = 0.0;
1794 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1795 gdouble dist_next = 0.0;
1796 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1798 gdouble dist_between_tps = fabs (dist_next - dist_current);
1799 gdouble ratio = 0.0;
1800 // Prevent division by 0 errors
1801 if ( dist_between_tps > 0.0 )
1802 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1804 if ( tp_current && tp_next ) {
1805 // Construct the name based on the distance value
1808 switch (dist_units) {
1809 case VIK_UNITS_DISTANCE_MILES:
1810 units = g_strdup ( _("miles") );
1812 // VIK_UNITS_DISTANCE_KILOMETRES:
1814 units = g_strdup ( _("km") );
1818 // Convert for display
1819 dist_i = distance_in_preferred_units ( dist_i );
1821 // Make the precision of the output related to the unit size.
1823 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1824 else if ( index == 1 )
1825 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1827 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1830 struct LatLon ll_current, ll_next;
1831 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1832 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1834 // positional interpolation
1835 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1836 // but should be good enough over the small scale that I anticipate usage on
1837 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1838 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1840 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1843 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1844 fgcolour = gdk_color_to_string ( &(trk->color) );
1846 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1848 // if highlight mode on, then colour the background in the highlight colour
1850 if ( drawing_highlight )
1851 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1853 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1855 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1857 g_free ( fgcolour );
1858 g_free ( bgcolour );
1865 * trw_layer_draw_track_name_labels:
1867 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1869 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1872 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1873 fgcolour = gdk_color_to_string ( &(trk->color) );
1875 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1877 // if highlight mode on, then colour the background in the highlight colour
1879 if ( drawing_highlight )
1880 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1882 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1884 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1886 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1887 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1888 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1889 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1890 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1891 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1893 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1895 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1898 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1899 // No other labels to draw
1902 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1905 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1908 VikCoord begin_coord = tp_begin->coord;
1909 VikCoord end_coord = tp_end->coord;
1911 gboolean done_start_end = FALSE;
1913 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1914 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1916 // This number can be configured via the settings if you really want to change it
1917 gdouble distance_diff;
1918 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1919 distance_diff = 100.0; // Metres
1921 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1922 // Start and end 'close' together so only draw one label at an average location
1923 gint x1, x2, y1, y2;
1924 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1925 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1927 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1929 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1930 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1933 done_start_end = TRUE;
1937 if ( ! done_start_end ) {
1938 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1939 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1940 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1941 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1942 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1943 g_free ( name_start );
1945 // Don't draw end label if this is the one being created
1946 if ( trk != dp->vtl->current_track ) {
1947 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1948 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1949 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1950 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1951 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1952 g_free ( name_end );
1957 g_free ( fgcolour );
1958 g_free ( bgcolour );
1962 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1964 if ( ! track->visible )
1967 /* TODO: this function is a mess, get rid of any redundancy */
1968 GList *list = track->trackpoints;
1970 gboolean useoldvals = TRUE;
1972 gboolean drawpoints;
1974 gboolean drawelevation;
1975 gdouble min_alt, max_alt, alt_diff = 0;
1977 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1978 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1981 if ( dp->vtl->drawelevation )
1983 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1984 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1985 alt_diff = max_alt - min_alt;
1988 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1989 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1990 trw_layer_draw_track ( id, track, dp, TRUE );
1992 if ( draw_track_outline )
1993 drawpoints = drawstops = FALSE;
1995 drawpoints = dp->vtl->drawpoints;
1996 drawstops = dp->vtl->drawstops;
1999 gboolean drawing_highlight = FALSE;
2000 /* Current track - used for creation */
2001 if ( track == dp->vtl->current_track )
2002 main_gc = dp->vtl->current_track_gc;
2004 if ( dp->highlight ) {
2005 /* Draw all tracks of the layer in special colour
2006 NB this supercedes the drawmode */
2007 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2008 drawing_highlight = TRUE;
2010 if ( !drawing_highlight ) {
2011 // Still need to figure out the gc according to the drawing mode:
2012 switch ( dp->vtl->drawmode ) {
2013 case DRAWMODE_BY_TRACK:
2014 if ( dp->vtl->track_1color_gc )
2015 g_object_unref ( dp->vtl->track_1color_gc );
2016 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2017 main_gc = dp->vtl->track_1color_gc;
2020 // Mostly for DRAWMODE_ALL_SAME_COLOR
2021 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2022 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2029 int x, y, oldx, oldy;
2030 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2032 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2034 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2036 // Draw the first point as something a bit different from the normal points
2037 // ATM it's slightly bigger and a triangle
2039 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2040 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2046 gdouble average_speed = 0.0;
2047 gdouble low_speed = 0.0;
2048 gdouble high_speed = 0.0;
2049 // If necessary calculate these values - which is done only once per track redraw
2050 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2051 // the percentage factor away from the average speed determines transistions between the levels
2052 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2053 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2054 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2057 while ((list = g_list_next(list)))
2059 tp = VIK_TRACKPOINT(list->data);
2060 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2062 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2063 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2064 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2065 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2066 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2068 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2071 * If points are the same in display coordinates, don't draw.
2073 if ( useoldvals && x == oldx && y == oldy )
2075 // Still need to process points to ensure 'stops' are drawn if required
2076 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2077 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2078 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 );
2083 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2084 if ( drawpoints || dp->vtl->drawlines ) {
2085 // setup main_gc for both point and line drawing
2086 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2087 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 ) );
2091 if ( drawpoints && ! draw_track_outline )
2096 * The concept of drawing stops is that a trackpoint
2097 * that is if the next trackpoint has a timestamp far into
2098 * the future, we draw a circle of 6x trackpoint size,
2099 * instead of a rectangle of 2x trackpoint size.
2100 * This is drawn first so the trackpoint will be drawn on top
2103 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2104 /* Stop point. Draw 6x circle. Always in redish colour */
2105 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 );
2107 /* Regular point - draw 2x square. */
2108 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2111 /* Final point - draw 4x circle. */
2112 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 );
2115 if ((!tp->newsegment) && (dp->vtl->drawlines))
2118 /* UTM only: zone check */
2119 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2120 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2123 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2125 if ( draw_track_outline ) {
2126 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2130 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2132 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2134 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2139 tmp[1].y = oldy-FIXALTITUDE(list->data);
2141 tmp[2].y = y-FIXALTITUDE(list->next->data);
2146 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2147 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2149 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2150 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2152 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2157 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2158 // Draw an arrow at the mid point to show the direction of the track
2159 // Code is a rework from vikwindow::draw_ruler()
2160 gint midx = (oldx + x) / 2;
2161 gint midy = (oldy + y) / 2;
2163 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2164 // Avoid divide by zero and ensure at least 1 pixel big
2166 gdouble dx = (oldx - midx) / len;
2167 gdouble dy = (oldy - midy) / len;
2168 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
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) );
2179 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2181 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2182 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2184 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2186 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2187 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 ));
2191 * If points are the same in display coordinates, don't draw.
2193 if ( x != oldx || y != oldy )
2195 if ( draw_track_outline )
2196 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2198 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2204 * If points are the same in display coordinates, don't draw.
2206 if ( x != oldx && y != oldy )
2208 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2209 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2217 // Labels drawn after the trackpoints, so the labels are on top
2218 if ( dp->vtl->track_draw_labels ) {
2219 if ( track->max_number_dist_labels > 0 ) {
2220 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2223 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2224 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2230 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2232 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2233 trw_layer_draw_track ( id, track, dp, FALSE );
2237 static void cached_pixbuf_free ( CachedPixbuf *cp )
2239 g_object_unref ( G_OBJECT(cp->pixbuf) );
2240 g_free ( cp->image );
2243 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2245 return strcmp ( cp->image, name );
2248 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2251 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2252 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2253 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2256 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2258 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2260 if ( wp->image && dp->vtl->drawimages )
2262 GdkPixbuf *pixbuf = NULL;
2265 if ( dp->vtl->image_alpha == 0)
2268 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2270 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2273 gchar *image = wp->image;
2274 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2275 if ( ! regularthumb )
2277 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2278 image = "\x12\x00"; /* this shouldn't occur naturally. */
2282 CachedPixbuf *cp = NULL;
2283 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2284 if ( dp->vtl->image_size == 128 )
2285 cp->pixbuf = regularthumb;
2288 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2289 g_assert ( cp->pixbuf );
2290 g_object_unref ( G_OBJECT(regularthumb) );
2292 cp->image = g_strdup ( image );
2294 /* needed so 'click picture' tool knows how big the pic is; we don't
2295 * store it in cp because they may have been freed already. */
2296 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2297 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2299 g_queue_push_head ( dp->vtl->image_cache, cp );
2300 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2301 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2303 pixbuf = cp->pixbuf;
2307 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2313 w = gdk_pixbuf_get_width ( pixbuf );
2314 h = gdk_pixbuf_get_height ( pixbuf );
2316 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2318 if ( dp->highlight ) {
2319 // Highlighted - so draw a little border around the chosen one
2320 // single line seems a little weak so draw 2 of them
2321 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2322 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2323 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2324 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2327 if ( dp->vtl->image_alpha == 255 )
2328 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2330 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2332 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2336 // Draw appropriate symbol - either symbol image or simple types
2337 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2338 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 );
2340 else if ( wp == dp->vtl->current_wp ) {
2341 switch ( dp->vtl->wp_symbol ) {
2342 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;
2343 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;
2344 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;
2345 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 );
2346 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 );
2351 switch ( dp->vtl->wp_symbol ) {
2352 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;
2353 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;
2354 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;
2355 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 );
2356 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;
2361 if ( dp->vtl->drawlabels )
2363 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2364 gint label_x, label_y;
2366 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2368 // Could this stored in the waypoint rather than recreating each pass?
2369 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2371 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2372 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2374 // Fallback if parse failure
2375 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2377 g_free ( wp_label_markup );
2379 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2380 label_x = x - width/2;
2381 if ( wp->symbol_pixbuf )
2382 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2384 label_y = y - dp->vtl->wp_size - height - 2;
2386 /* if highlight mode on, then draw background text in highlight colour */
2387 if ( dp->highlight )
2388 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2390 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2391 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2396 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2398 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2399 trw_layer_draw_waypoint ( id, wp, dp );
2403 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2405 static struct DrawingParams dp;
2406 g_assert ( l != NULL );
2408 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2410 if ( l->tracks_visible )
2411 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2413 if ( l->routes_visible )
2414 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2416 if (l->waypoints_visible)
2417 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2420 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2422 // 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
2423 // This may seem slightly inefficient to test each time for every layer
2424 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2425 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2426 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2428 trw_layer_draw_with_highlight ( l, data, FALSE );
2431 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2433 // Check the layer for visibility (including all the parents visibilities)
2434 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2436 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2440 * vik_trw_layer_draw_highlight_item:
2442 * Only handles a single track or waypoint ATM
2443 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2445 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2447 // Check the layer for visibility (including all the parents visibilities)
2448 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2451 static struct DrawingParams dp;
2452 init_drawing_params ( &dp, vtl, vvp, TRUE );
2455 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2457 trw_layer_draw_track_cb ( NULL, trk, &dp );
2459 if ( vtl->waypoints_visible && wpt ) {
2460 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2465 * vik_trw_layer_draw_highlight_item:
2467 * Generally for drawing all tracks or routes or waypoints
2468 * trks may be actually routes
2469 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2471 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2473 // Check the layer for visibility (including all the parents visibilities)
2474 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2477 static struct DrawingParams dp;
2478 init_drawing_params ( &dp, vtl, vvp, TRUE );
2481 gboolean is_routes = (trks == vtl->routes);
2482 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2484 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2487 if ( vtl->waypoints_visible && wpts )
2488 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2491 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2494 if ( vtl->track_bg_gc )
2496 g_object_unref ( vtl->track_bg_gc );
2497 vtl->track_bg_gc = NULL;
2499 if ( vtl->track_1color_gc )
2501 g_object_unref ( vtl->track_1color_gc );
2502 vtl->track_1color_gc = NULL;
2504 if ( vtl->current_track_gc )
2506 g_object_unref ( vtl->current_track_gc );
2507 vtl->current_track_gc = NULL;
2509 if ( vtl->current_track_newpoint_gc )
2511 g_object_unref ( vtl->current_track_newpoint_gc );
2512 vtl->current_track_newpoint_gc = NULL;
2515 if ( ! vtl->track_gc )
2517 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2518 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2519 g_array_free ( vtl->track_gc, TRUE );
2520 vtl->track_gc = NULL;
2523 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2525 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2526 gint width = vtl->line_thickness;
2528 if ( vtl->track_gc )
2529 trw_layer_free_track_gcs ( vtl );
2531 if ( vtl->track_bg_gc )
2532 g_object_unref ( vtl->track_bg_gc );
2533 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2535 // Ensure new track drawing heeds line thickness setting
2536 // however always have a minium of 2, as 1 pixel is really narrow
2537 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2539 if ( vtl->current_track_gc )
2540 g_object_unref ( vtl->current_track_gc );
2541 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2542 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2544 // 'newpoint' gc is exactly the same as the current track gc
2545 if ( vtl->current_track_newpoint_gc )
2546 g_object_unref ( vtl->current_track_newpoint_gc );
2547 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2548 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2550 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2552 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2553 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2555 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2556 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2557 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2559 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2561 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2564 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2566 VikTrwLayer *rv = trw_layer_new1 ( vp );
2567 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2569 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2570 /* early exit, as the rest is GUI related */
2574 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2575 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2577 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2578 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2580 trw_layer_new_track_gcs ( rv, vp );
2582 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2583 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2584 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2585 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2587 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2589 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2594 #define SMALL_ICON_SIZE 18
2596 * Can accept a null symbol, and may return null value
2598 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2600 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2601 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2602 // So needing a small icon for the treeview may need some resizing:
2603 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2604 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2608 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2610 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2612 GdkPixbuf *pixbuf = NULL;
2614 if ( track->has_color ) {
2615 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2616 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2617 // Here is some magic found to do the conversion
2618 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2619 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2620 ((track->color.green & 0xff00) << 8) |
2621 (track->color.blue & 0xff00);
2623 gdk_pixbuf_fill ( pixbuf, pixel );
2626 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 );
2629 g_object_unref (pixbuf);
2631 *new_iter = *((GtkTreeIter *) pass_along[1]);
2632 if ( track->is_route )
2633 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2635 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2637 if ( ! track->visible )
2638 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2641 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2643 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2645 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 );
2647 *new_iter = *((GtkTreeIter *) pass_along[1]);
2648 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2650 if ( ! wp->visible )
2651 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2654 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2656 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2659 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2661 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2664 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2666 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2669 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2672 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2674 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2675 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2677 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2679 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2682 if ( g_hash_table_size (vtl->routes) > 0 ) {
2683 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2685 pass_along[0] = &(vtl->routes_iter);
2686 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2688 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2690 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2693 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2694 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2696 pass_along[0] = &(vtl->waypoints_iter);
2697 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2699 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2701 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2706 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2710 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2711 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2712 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2713 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2715 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2717 return (t->visible ^= 1);
2721 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2723 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2725 return (t->visible ^= 1);
2729 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2731 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2733 return (t->visible ^= 1);
2743 * Return a property about tracks for this layer
2745 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2747 return vtl->line_thickness;
2750 // Structure to hold multiple track information for a layer
2759 * Build up layer multiple track information via updating the tooltip_tracks structure
2761 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2763 tt->length = tt->length + vik_track_get_length (tr);
2765 // Ensure times are available
2766 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2767 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2768 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2769 if ( trkpt_last->has_timestamp ) {
2771 t1 = vik_track_get_tp_first(tr)->timestamp;
2772 t2 = trkpt_last->timestamp;
2774 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2775 // Hence initialize to the first 'proper' value
2776 if ( tt->start_time == 0 )
2777 tt->start_time = t1;
2778 if ( tt->end_time == 0 )
2781 // Update find the earliest / last times
2782 if ( t1 < tt->start_time )
2783 tt->start_time = t1;
2784 if ( t2 > tt->end_time )
2787 // Keep track of total time
2788 // there maybe gaps within a track (eg segments)
2789 // but this should be generally good enough for a simple indicator
2790 tt->duration = tt->duration + (int)(t2-t1);
2796 * Generate tooltip text for the layer.
2797 * This is relatively complicated as it considers information for
2798 * no tracks, a single track or multiple tracks
2799 * (which may or may not have timing information)
2801 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2812 static gchar tmp_buf[128];
2815 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2817 // Safety check - I think these should always be valid
2818 if ( vtl->tracks && vtl->waypoints ) {
2819 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2820 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2822 GDate* gdate_start = g_date_new ();
2823 g_date_set_time_t (gdate_start, tt.start_time);
2825 GDate* gdate_end = g_date_new ();
2826 g_date_set_time_t (gdate_end, tt.end_time);
2828 if ( g_date_compare (gdate_start, gdate_end) ) {
2829 // Dates differ so print range on separate line
2830 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2831 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2832 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2835 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2836 if ( tt.start_time != 0 )
2837 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2841 if ( tt.length > 0.0 ) {
2842 gdouble len_in_units;
2844 // Setup info dependent on distance units
2845 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2846 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2847 len_in_units = VIK_METERS_TO_MILES(tt.length);
2850 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2851 len_in_units = tt.length/1000.0;
2854 // Timing information if available
2856 if ( tt.duration > 0 ) {
2857 g_snprintf (tbuf1, sizeof(tbuf1),
2858 _(" in %d:%02d hrs:mins"),
2859 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2861 g_snprintf (tbuf2, sizeof(tbuf2),
2862 _("\n%sTotal Length %.1f %s%s"),
2863 tbuf3, len_in_units, tbuf4, tbuf1);
2866 // Put together all the elements to form compact tooltip text
2867 g_snprintf (tmp_buf, sizeof(tmp_buf),
2868 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2869 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2871 g_date_free (gdate_start);
2872 g_date_free (gdate_end);
2879 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2883 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2885 // Very simple tooltip - may expand detail in the future...
2886 static gchar tmp_buf[32];
2887 g_snprintf (tmp_buf, sizeof(tmp_buf),
2889 g_hash_table_size (l->tracks));
2893 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2895 // Very simple tooltip - may expand detail in the future...
2896 static gchar tmp_buf[32];
2897 g_snprintf (tmp_buf, sizeof(tmp_buf),
2899 g_hash_table_size (l->routes));
2904 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2905 // Same tooltip for a route
2906 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2909 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2910 tr = g_hash_table_lookup ( l->tracks, sublayer );
2912 tr = g_hash_table_lookup ( l->routes, sublayer );
2915 // Could be a better way of handling strings - but this works...
2916 gchar time_buf1[20];
2917 gchar time_buf2[20];
2918 time_buf1[0] = '\0';
2919 time_buf2[0] = '\0';
2920 static gchar tmp_buf[100];
2921 // Compact info: Short date eg (11/20/99), duration and length
2922 // Hopefully these are the things that are most useful and so promoted into the tooltip
2923 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2924 // %x The preferred date representation for the current locale without the time.
2925 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2926 time_t dur = vik_track_get_duration ( tr );
2928 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2930 // Get length and consider the appropriate distance units
2931 gdouble tr_len = vik_track_get_length(tr);
2932 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2933 switch (dist_units) {
2934 case VIK_UNITS_DISTANCE_KILOMETRES:
2935 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2937 case VIK_UNITS_DISTANCE_MILES:
2938 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2947 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2949 // Very simple tooltip - may expand detail in the future...
2950 static gchar tmp_buf[32];
2951 g_snprintf (tmp_buf, sizeof(tmp_buf),
2953 g_hash_table_size (l->waypoints));
2957 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2959 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2960 // NB It's OK to return NULL
2965 return w->description;
2974 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2977 * set_statusbar_msg_info_trkpt:
2979 * Function to show track point information on the statusbar
2980 * Items displayed is controlled by the settings format code
2982 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2984 gchar *statusbar_format_code = NULL;
2985 gboolean need2free = FALSE;
2986 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2987 // Otherwise use default
2988 statusbar_format_code = g_strdup ( "KEATDN" );
2992 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2993 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2997 g_free ( statusbar_format_code );
3001 * Function to show basic waypoint information on the statusbar
3003 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3006 switch (a_vik_get_units_height ()) {
3007 case VIK_UNITS_HEIGHT_FEET:
3008 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3011 //VIK_UNITS_HEIGHT_METRES:
3012 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3016 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3017 // one can easily use the current pointer position to see this if needed
3018 gchar *lat = NULL, *lon = NULL;
3019 static struct LatLon ll;
3020 vik_coord_to_latlon (&(wpt->coord), &ll);
3021 a_coords_latlon_to_string ( &ll, &lat, &lon );
3023 // Combine parts to make overall message
3026 // Add comment if available
3027 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3029 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3030 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3037 * General layer selection function, find out which bit is selected and take appropriate action
3039 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3042 l->current_wp = NULL;
3043 l->current_wp_id = NULL;
3044 trw_layer_cancel_current_tp ( l, FALSE );
3047 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3051 case VIK_TREEVIEW_TYPE_LAYER:
3053 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3054 /* Mark for redraw */
3059 case VIK_TREEVIEW_TYPE_SUBLAYER:
3063 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3065 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3066 /* Mark for redraw */
3070 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3072 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3073 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3074 /* Mark for redraw */
3078 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3080 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3081 /* Mark for redraw */
3085 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3087 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3088 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3089 /* Mark for redraw */
3093 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3095 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3096 /* Mark for redraw */
3100 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3102 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3104 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3105 // Show some waypoint info
3106 set_statusbar_msg_info_wpt ( l, wpt );
3107 /* Mark for redraw */
3114 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3123 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3128 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3133 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3138 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3140 return l->waypoints;
3143 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3145 return vtl->tracks_iters;
3148 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3150 return vtl->routes_iters;
3153 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3155 return vtl->waypoints;
3158 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3160 return ! ( g_hash_table_size ( vtl->tracks ) ||
3161 g_hash_table_size ( vtl->routes ) ||
3162 g_hash_table_size ( vtl->waypoints ) );
3165 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3167 return vtl->tracks_visible;
3170 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3172 return vtl->routes_visible;
3175 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3177 return vtl->waypoints_visible;
3181 * ATM use a case sensitive find
3182 * Finds the first one
3184 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3186 if ( wp && wp->name )
3187 if ( ! strcmp ( wp->name, name ) )
3193 * Get waypoint by name - not guaranteed to be unique
3194 * Finds the first one
3196 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3198 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3202 * ATM use a case sensitive find
3203 * Finds the first one
3205 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3207 if ( trk && trk->name )
3208 if ( ! strcmp ( trk->name, name ) )
3214 * Get track by name - not guaranteed to be unique
3215 * Finds the first one
3217 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3219 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3223 * Get route by name - not guaranteed to be unique
3224 * Finds the first one
3226 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3228 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3231 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3233 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3234 maxmin[0].lat = trk->bbox.north;
3235 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3236 maxmin[1].lat = trk->bbox.south;
3237 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3238 maxmin[0].lon = trk->bbox.east;
3239 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3240 maxmin[1].lon = trk->bbox.west;
3243 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3245 // Continually reuse maxmin to find the latest maximum and minimum values
3246 // First set to waypoints bounds
3247 maxmin[0].lat = vtl->waypoints_bbox.north;
3248 maxmin[1].lat = vtl->waypoints_bbox.south;
3249 maxmin[0].lon = vtl->waypoints_bbox.east;
3250 maxmin[1].lon = vtl->waypoints_bbox.west;
3251 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3252 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3255 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3257 /* 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... */
3258 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3259 trw_layer_find_maxmin (vtl, maxmin);
3260 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3264 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3265 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3270 static void trw_layer_centerize ( menu_array_layer values )
3272 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3274 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3275 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3277 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3280 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3282 /* First set the center [in case previously viewing from elsewhere] */
3283 /* Then loop through zoom levels until provided positions are in view */
3284 /* This method is not particularly fast - but should work well enough */
3285 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3287 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3288 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3290 /* Convert into definite 'smallest' and 'largest' positions */
3291 struct LatLon minmin;
3292 if ( maxmin[0].lat < maxmin[1].lat )
3293 minmin.lat = maxmin[0].lat;
3295 minmin.lat = maxmin[1].lat;
3297 struct LatLon maxmax;
3298 if ( maxmin[0].lon > maxmin[1].lon )
3299 maxmax.lon = maxmin[0].lon;
3301 maxmax.lon = maxmin[1].lon;
3303 /* Never zoom in too far - generally not that useful, as too close ! */
3304 /* Always recalculate the 'best' zoom level */
3306 vik_viewport_set_zoom ( vvp, zoom );
3308 gdouble min_lat, max_lat, min_lon, max_lon;
3309 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3310 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3311 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3312 /* NB I think the logic used in this test to determine if the bounds is within view
3313 fails if track goes across 180 degrees longitude.
3314 Hopefully that situation is not too common...
3315 Mind you viking doesn't really do edge locations to well anyway */
3316 if ( min_lat < minmin.lat &&
3317 max_lat > minmin.lat &&
3318 min_lon < maxmax.lon &&
3319 max_lon > maxmax.lon )
3320 /* Found within zoom level */
3325 vik_viewport_set_zoom ( vvp, zoom );
3329 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3331 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3332 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3333 trw_layer_find_maxmin (vtl, maxmin);
3334 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3337 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3342 static void trw_layer_auto_view ( menu_array_layer values )
3344 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3345 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3346 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3347 vik_layers_panel_emit_update ( vlp );
3350 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3353 static void trw_layer_export_gpspoint ( menu_array_layer values )
3355 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3357 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3359 g_free ( auto_save_name );
3362 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3364 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3366 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3368 g_free ( auto_save_name );
3371 static void trw_layer_export_gpx ( menu_array_layer values )
3373 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3375 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3377 g_free ( auto_save_name );
3380 static void trw_layer_export_kml ( menu_array_layer values )
3382 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3384 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3386 g_free ( auto_save_name );
3389 static void trw_layer_export_geojson ( menu_array_layer values )
3391 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3393 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3395 g_free ( auto_save_name );
3398 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3400 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3401 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3404 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3406 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3409 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3411 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3414 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3416 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3418 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3419 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3421 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3423 if ( !trk || !trk->name )
3426 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3428 gchar *label = NULL;
3429 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3430 label = _("Export Route as GPX");
3432 label = _("Export Track as GPX");
3433 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3435 g_free ( auto_save_name );
3438 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3440 wpu_udata *user_data = udata;
3441 if ( wp == user_data->wp ) {
3442 user_data->uuid = id;
3448 static void trw_layer_goto_wp ( menu_array_layer values )
3450 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3451 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3452 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3453 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3454 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3456 GTK_RESPONSE_REJECT,
3458 GTK_RESPONSE_ACCEPT,
3461 GtkWidget *label, *entry;
3462 label = gtk_label_new(_("Waypoint Name:"));
3463 entry = gtk_entry_new();
3465 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3466 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3467 gtk_widget_show_all ( label );
3468 gtk_widget_show_all ( entry );
3470 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3472 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3474 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3475 // Find *first* wp with the given name
3476 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3479 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3482 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3483 vik_layers_panel_emit_update ( vlp );
3485 // Find and select on the side panel
3490 // Hmmm, want key of it
3491 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3493 if ( wpf && udata.uuid ) {
3494 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3495 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3504 gtk_widget_destroy ( dia );
3507 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3509 gchar *default_name = highest_wp_number_get(vtl);
3510 VikWaypoint *wp = vik_waypoint_new();
3511 gchar *returned_name;
3513 wp->coord = *def_coord;
3515 // Attempt to auto set height if DEM data is available
3516 vik_waypoint_apply_dem_data ( wp, TRUE );
3518 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3520 if ( returned_name )
3523 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3524 g_free (default_name);
3525 g_free (returned_name);
3528 g_free (default_name);
3529 vik_waypoint_free(wp);
3533 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3535 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3536 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3537 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3538 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3539 VikViewport *vvp = vik_window_viewport(vw);
3541 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3542 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3543 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3544 trw_layer_calculate_bounds_waypoints ( vtl );
3545 vik_layers_panel_emit_update ( vlp );
3548 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3550 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3551 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3552 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3554 trw_layer_find_maxmin (vtl, maxmin);
3555 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3556 trw_layer_calculate_bounds_waypoints ( vtl );
3557 vik_layers_panel_emit_update ( vlp );
3560 #ifdef VIK_CONFIG_GEOTAG
3561 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3563 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3565 // Update directly - not changing the mtime
3566 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3569 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3571 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3574 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3578 * Use code in separate file for this feature as reasonably complex
3580 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3582 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3583 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3584 // Unset so can be reverified later if necessary
3585 vtl->has_verified_thumbnails = FALSE;
3587 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3593 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3595 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3596 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3598 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3604 static void trw_layer_geotagging ( menu_array_layer values )
3606 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3607 // Unset so can be reverified later if necessary
3608 vtl->has_verified_thumbnails = FALSE;
3610 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3617 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3619 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3621 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3622 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3623 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3624 VikViewport *vvp = vik_window_viewport(vw);
3626 vik_datasource_mode_t mode = datasource->mode;
3627 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3628 mode = VIK_DATASOURCE_ADDTOLAYER;
3629 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3633 * Acquire into this TRW Layer straight from GPS Device
3635 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3637 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3641 * Acquire into this TRW Layer from Directions
3643 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3645 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3649 * Acquire into this TRW Layer from an entered URL
3651 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3653 trw_layer_acquire ( values, &vik_datasource_url_interface );
3656 #ifdef VIK_CONFIG_OPENSTREETMAP
3658 * Acquire into this TRW Layer from OSM
3660 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3662 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3666 * Acquire into this TRW Layer from OSM for 'My' Traces
3668 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3670 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3674 #ifdef VIK_CONFIG_GEOCACHES
3676 * Acquire into this TRW Layer from Geocaching.com
3678 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3680 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3684 #ifdef VIK_CONFIG_GEOTAG
3686 * Acquire into this TRW Layer from images
3688 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3690 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3692 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3694 // Reverify thumbnails as they may have changed
3695 vtl->has_verified_thumbnails = FALSE;
3696 trw_layer_verify_thumbnails ( vtl, NULL );
3701 * Acquire into this TRW Layer from any GPS Babel supported file
3703 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3705 trw_layer_acquire ( values, &vik_datasource_file_interface );
3708 static void trw_layer_gps_upload ( menu_array_layer values )
3710 menu_array_sublayer data;
3712 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3714 data[MA_VTL] = values[MA_VTL];
3715 data[MA_VLP] = values[MA_VLP];
3717 trw_layer_gps_upload_any ( data );
3721 * If pass_along[3] is defined that this will upload just that track
3723 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3725 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3726 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3728 // May not actually get a track here as values[2&3] can be null
3729 VikTrack *track = NULL;
3730 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3731 gboolean xfer_all = FALSE;
3733 if ( values[MA_SUBTYPE] ) {
3735 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3736 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3739 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3740 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3743 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3746 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3750 else if ( !values[MA_CONFIRM] )
3751 xfer_all = TRUE; // i.e. whole layer
3753 if (track && !track->visible) {
3754 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3758 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3759 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3760 GTK_DIALOG_DESTROY_WITH_PARENT,
3762 GTK_RESPONSE_ACCEPT,
3764 GTK_RESPONSE_REJECT,
3767 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3768 GtkWidget *response_w = NULL;
3769 #if GTK_CHECK_VERSION (2, 20, 0)
3770 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3774 gtk_widget_grab_focus ( response_w );
3776 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3778 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3779 datasource_gps_clean_up ( dgs );
3780 gtk_widget_destroy ( dialog );
3784 // Get info from reused datasource dialog widgets
3785 gchar* protocol = datasource_gps_get_protocol ( dgs );
3786 gchar* port = datasource_gps_get_descriptor ( dgs );
3787 // NB don't free the above strings as they're references to values held elsewhere
3788 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3789 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3790 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3791 gboolean turn_off = datasource_gps_get_off ( dgs );
3793 gtk_widget_destroy ( dialog );
3795 // When called from the viewport - work the corresponding layerspanel:
3797 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3800 // Apply settings to transfer to the GPS device
3807 vik_layers_panel_get_viewport (vlp),
3815 static void trw_layer_new_wp ( menu_array_layer values )
3817 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3818 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3819 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3820 instead return true if you want to update. */
3821 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 ) {
3822 trw_layer_calculate_bounds_waypoints ( vtl );
3823 vik_layers_panel_emit_update ( vlp );
3827 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3829 vtl->current_track = vik_track_new();
3830 vik_track_set_defaults ( vtl->current_track );
3831 vtl->current_track->visible = TRUE;
3832 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3833 // Create track with the preferred colour from the layer properties
3834 vtl->current_track->color = vtl->track_color;
3836 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3837 vtl->current_track->has_color = TRUE;
3838 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3841 static void trw_layer_new_track ( menu_array_layer values )
3843 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3845 if ( ! vtl->current_track ) {
3846 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3847 new_track_create_common ( vtl, name );
3850 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3854 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3856 vtl->current_track = vik_track_new();
3857 vik_track_set_defaults ( vtl->current_track );
3858 vtl->current_track->visible = TRUE;
3859 vtl->current_track->is_route = TRUE;
3860 // By default make all routes red
3861 vtl->current_track->has_color = TRUE;
3862 gdk_color_parse ( "red", &vtl->current_track->color );
3863 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3866 static void trw_layer_new_route ( menu_array_layer values )
3868 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3870 if ( ! vtl->current_track ) {
3871 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3872 new_route_create_common ( vtl, name );
3874 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3878 static void trw_layer_auto_routes_view ( menu_array_layer values )
3880 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3881 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3883 if ( g_hash_table_size (vtl->routes) > 0 ) {
3884 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3885 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3886 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3887 vik_layers_panel_emit_update ( vlp );
3892 static void trw_layer_finish_track ( menu_array_layer values )
3894 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3895 vtl->current_track = NULL;
3896 vtl->route_finder_started = FALSE;
3897 vik_layer_emit_update ( VIK_LAYER(vtl) );
3900 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3902 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3903 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3905 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3906 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3907 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3908 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3909 vik_layers_panel_emit_update ( vlp );
3913 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3915 /* NB do not care if wp is visible or not */
3916 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3919 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3921 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3922 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3924 /* Only 1 waypoint - jump straight to it */
3925 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3926 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3927 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3929 /* If at least 2 waypoints - find center and then zoom to fit */
3930 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3932 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3933 maxmin[0].lat = vtl->waypoints_bbox.north;
3934 maxmin[1].lat = vtl->waypoints_bbox.south;
3935 maxmin[0].lon = vtl->waypoints_bbox.east;
3936 maxmin[1].lon = vtl->waypoints_bbox.west;
3937 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3940 vik_layers_panel_emit_update ( vlp );
3943 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3945 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3948 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3950 if ( values[MA_MISC] ) {
3951 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3952 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3956 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3958 static menu_array_layer pass_along;
3960 GtkWidget *export_submenu;
3961 pass_along[MA_VTL] = vtl;
3962 pass_along[MA_VLP] = vlp;
3964 item = gtk_menu_item_new();
3965 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3966 gtk_widget_show ( item );
3968 if ( vtl->current_track ) {
3969 if ( vtl->current_track->is_route )
3970 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3972 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3974 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3975 gtk_widget_show ( item );
3978 item = gtk_menu_item_new ();
3979 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3980 gtk_widget_show ( item );
3983 /* Now with icons */
3984 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3985 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3988 gtk_widget_show ( item );
3990 GtkWidget *view_submenu = gtk_menu_new();
3991 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3992 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3993 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3994 gtk_widget_show ( item );
3995 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3997 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3999 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4000 gtk_widget_show ( item );
4002 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4004 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4005 gtk_widget_show ( item );
4007 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4010 gtk_widget_show ( item );
4012 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4013 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4015 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4016 gtk_widget_show ( item );
4018 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4020 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4021 gtk_widget_show ( item );
4023 export_submenu = gtk_menu_new ();
4024 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4025 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4026 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4027 gtk_widget_show ( item );
4028 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4030 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4032 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4033 gtk_widget_show ( item );
4035 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4037 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4038 gtk_widget_show ( item );
4040 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4042 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4043 gtk_widget_show ( item );
4045 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4046 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4047 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4048 gtk_widget_show ( item );
4050 if ( have_geojson_export ) {
4051 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4053 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4054 gtk_widget_show ( item );
4057 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4059 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4060 gtk_widget_show ( item );
4062 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4063 item = gtk_menu_item_new_with_mnemonic ( external1 );
4064 g_free ( external1 );
4065 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4066 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4067 gtk_widget_show ( item );
4069 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4070 item = gtk_menu_item_new_with_mnemonic ( external2 );
4071 g_free ( external2 );
4072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4073 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4074 gtk_widget_show ( item );
4076 GtkWidget *new_submenu = gtk_menu_new();
4077 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4078 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4079 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4080 gtk_widget_show(item);
4081 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4083 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4085 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4086 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4087 gtk_widget_show ( item );
4089 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4090 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4092 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4093 gtk_widget_show ( item );
4094 // Make it available only when a new track *not* already in progress
4095 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4097 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4100 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4101 gtk_widget_show ( item );
4102 // Make it available only when a new track *not* already in progress
4103 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4105 #ifdef VIK_CONFIG_GEOTAG
4106 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4108 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4109 gtk_widget_show ( item );
4112 GtkWidget *acquire_submenu = gtk_menu_new ();
4113 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4115 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4116 gtk_widget_show ( item );
4117 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4119 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4121 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4122 gtk_widget_show ( item );
4124 /* FIXME: only add menu when at least a routing engine has support for Directions */
4125 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4127 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4128 gtk_widget_show ( item );
4130 #ifdef VIK_CONFIG_OPENSTREETMAP
4131 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4133 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4134 gtk_widget_show ( item );
4136 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4137 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4138 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4139 gtk_widget_show ( item );
4142 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4144 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4145 gtk_widget_show ( item );
4147 #ifdef VIK_CONFIG_GEONAMES
4148 GtkWidget *wikipedia_submenu = gtk_menu_new();
4149 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4150 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4151 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4152 gtk_widget_show(item);
4153 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4155 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4157 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4158 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4159 gtk_widget_show ( item );
4161 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4162 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4164 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4165 gtk_widget_show ( item );
4168 #ifdef VIK_CONFIG_GEOCACHES
4169 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4171 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4172 gtk_widget_show ( item );
4175 #ifdef VIK_CONFIG_GEOTAG
4176 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4177 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4178 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4179 gtk_widget_show ( item );
4182 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4183 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4184 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4185 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4186 gtk_widget_show ( item );
4188 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4190 GtkWidget *upload_submenu = gtk_menu_new ();
4191 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4192 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4193 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4194 gtk_widget_show ( item );
4195 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4197 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4198 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4200 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4201 gtk_widget_show ( item );
4203 #ifdef VIK_CONFIG_OPENSTREETMAP
4204 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4207 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4208 gtk_widget_show ( item );
4211 GtkWidget *delete_submenu = gtk_menu_new ();
4212 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4214 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4215 gtk_widget_show ( item );
4216 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4218 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4221 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4222 gtk_widget_show ( item );
4224 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4225 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4227 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4228 gtk_widget_show ( item );
4230 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4233 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4234 gtk_widget_show ( item );
4236 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4237 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4238 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4239 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4240 gtk_widget_show ( item );
4242 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4243 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4244 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4245 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4246 gtk_widget_show ( item );
4248 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4249 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4250 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4251 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4252 gtk_widget_show ( item );
4254 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4255 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4257 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4258 gtk_widget_show ( item );
4261 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4262 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4264 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4265 gtk_widget_show ( item );
4268 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4269 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4272 gtk_widget_show ( item );
4273 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4275 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4278 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4279 gtk_widget_show ( item );
4280 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4283 // Fake Waypoint UUIDs vi simple increasing integer
4284 static guint wp_uuid = 0;
4286 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4290 vik_waypoint_set_name (wp, name);
4292 if ( VIK_LAYER(vtl)->realized )
4294 // Do we need to create the sublayer:
4295 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4296 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4299 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4301 // Visibility column always needed for waypoints
4302 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 );
4304 // Actual setting of visibility dependent on the waypoint
4305 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4307 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4309 // Sort now as post_read is not called on a realized waypoint
4310 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4313 highest_wp_number_add_wp(vtl, name);
4314 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4318 // Fake Track UUIDs vi simple increasing integer
4319 static guint tr_uuid = 0;
4321 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4325 vik_track_set_name (t, name);
4327 if ( VIK_LAYER(vtl)->realized )
4329 // Do we need to create the sublayer:
4330 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4331 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4334 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4335 // Visibility column always needed for tracks
4336 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 );
4338 // Actual setting of visibility dependent on the track
4339 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4341 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4343 // Sort now as post_read is not called on a realized track
4344 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4347 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4349 trw_layer_update_treeview ( vtl, t );
4352 // Fake Route UUIDs vi simple increasing integer
4353 static guint rt_uuid = 0;
4355 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4359 vik_track_set_name (t, name);
4361 if ( VIK_LAYER(vtl)->realized )
4363 // Do we need to create the sublayer:
4364 if ( g_hash_table_size (vtl->routes) == 0 ) {
4365 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4368 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4369 // Visibility column always needed for routes
4370 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 );
4371 // Actual setting of visibility dependent on the route
4372 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4374 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4376 // Sort now as post_read is not called on a realized route
4377 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4380 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4382 trw_layer_update_treeview ( vtl, t );
4385 /* to be called whenever a track has been deleted or may have been changed. */
4386 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4388 if (vtl->current_tp_track == trk )
4389 trw_layer_cancel_current_tp ( vtl, FALSE );
4393 * Normally this is done to due the waypoint size preference having changed
4395 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4397 GHashTableIter iter;
4398 gpointer key, value;
4401 g_hash_table_iter_init ( &iter, vtl->waypoints );
4402 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4403 VikWaypoint *wp = VIK_WAYPOINT(value);
4405 // Reapply symbol setting to update the pixbuf
4406 gchar *tmp_symbol = g_strdup ( wp->symbol );
4407 vik_waypoint_set_symbol ( wp, tmp_symbol );
4408 g_free ( tmp_symbol );
4414 * trw_layer_new_unique_sublayer_name:
4416 * Allocates a unique new name
4418 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4421 gchar *newname = g_strdup(name);
4426 switch ( sublayer_type ) {
4427 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4428 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4430 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4431 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4434 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4437 // If found a name already in use try adding 1 to it and we try again
4439 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4441 newname = new_newname;
4444 } while ( id != NULL);
4449 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4451 // No more uniqueness of name forced when loading from a file
4452 // This now makes this function a little redunant as we just flow the parameters through
4453 vik_trw_layer_add_waypoint ( vtl, name, wp );
4456 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4458 if ( vtl->route_finder_append && vtl->current_track ) {
4459 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4461 // enforce end of current track equal to start of tr
4462 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4463 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4464 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4465 vik_track_add_trackpoint ( vtl->current_track,
4466 vik_trackpoint_copy ( cur_end ),
4470 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4471 vik_track_free ( tr );
4472 vtl->route_finder_append = FALSE; /* this means we have added it */
4475 // No more uniqueness of name forced when loading from a file
4477 vik_trw_layer_add_route ( vtl, name, tr );
4479 vik_trw_layer_add_track ( vtl, name, tr );
4481 if ( vtl->route_finder_check_added_track ) {
4482 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4483 vtl->route_finder_added_track = tr;
4488 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4490 *l = g_list_append(*l, id);
4494 * Move an item from one TRW layer to another TRW layer
4496 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4498 // TODO reconsider strategy when moving within layer (if anything...)
4499 gboolean rename = ( vtl_src != vtl_dest );
4503 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4504 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4508 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4510 newname = g_strdup ( trk->name );
4512 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4513 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4515 vik_trw_layer_delete_track ( vtl_src, trk );
4518 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4519 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4523 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4525 newname = g_strdup ( trk->name );
4527 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4528 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4530 vik_trw_layer_delete_route ( vtl_src, trk );
4533 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4534 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4538 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4540 newname = g_strdup ( wp->name );
4542 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4543 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4545 trw_layer_delete_waypoint ( vtl_src, wp );
4547 // Recalculate bounds even if not renamed as maybe dragged between layers
4548 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4549 trw_layer_calculate_bounds_waypoints ( vtl_src );
4553 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4555 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4556 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4558 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4559 GList *items = NULL;
4562 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4563 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4565 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4566 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4568 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4569 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4574 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4575 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4576 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4577 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4579 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4586 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4587 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4591 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4593 trku_udata *user_data = udata;
4594 if ( trk == user_data->trk ) {
4595 user_data->uuid = id;
4601 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4603 gboolean was_visible = FALSE;
4604 if ( trk && trk->name ) {
4606 if ( trk == vtl->current_track ) {
4607 vtl->current_track = NULL;
4608 vtl->current_tp_track = NULL;
4609 vtl->current_tp_id = NULL;
4610 vtl->moving_tp = FALSE;
4611 vtl->route_finder_started = FALSE;
4614 was_visible = trk->visible;
4616 if ( trk == vtl->route_finder_added_track )
4617 vtl->route_finder_added_track = NULL;
4623 // Hmmm, want key of it
4624 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4626 if ( trkf && udata.uuid ) {
4627 /* could be current_tp, so we have to check */
4628 trw_layer_cancel_tps_of_track ( vtl, trk );
4630 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4633 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4634 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4635 g_hash_table_remove ( vtl->tracks, udata.uuid );
4637 // If last sublayer, then remove sublayer container
4638 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4639 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4642 // Incase it was selected (no item delete signal ATM)
4643 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4649 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4651 gboolean was_visible = FALSE;
4653 if ( trk && trk->name ) {
4655 if ( trk == vtl->current_track ) {
4656 vtl->current_track = NULL;
4657 vtl->current_tp_track = NULL;
4658 vtl->current_tp_id = NULL;
4659 vtl->moving_tp = FALSE;
4662 was_visible = trk->visible;
4664 if ( trk == vtl->route_finder_added_track )
4665 vtl->route_finder_added_track = NULL;
4671 // Hmmm, want key of it
4672 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4674 if ( trkf && udata.uuid ) {
4675 /* could be current_tp, so we have to check */
4676 trw_layer_cancel_tps_of_track ( vtl, trk );
4678 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4681 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4682 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4683 g_hash_table_remove ( vtl->routes, udata.uuid );
4685 // If last sublayer, then remove sublayer container
4686 if ( g_hash_table_size (vtl->routes) == 0 ) {
4687 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4690 // Incase it was selected (no item delete signal ATM)
4691 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4697 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4699 gboolean was_visible = FALSE;
4701 if ( wp && wp->name ) {
4703 if ( wp == vtl->current_wp ) {
4704 vtl->current_wp = NULL;
4705 vtl->current_wp_id = NULL;
4706 vtl->moving_wp = FALSE;
4709 was_visible = wp->visible;
4715 // Hmmm, want key of it
4716 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4718 if ( wpf && udata.uuid ) {
4719 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4722 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4723 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4725 highest_wp_number_remove_wp(vtl, wp->name);
4726 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4728 // If last sublayer, then remove sublayer container
4729 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4730 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4733 // Incase it was selected (no item delete signal ATM)
4734 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4742 // Only for temporary use by trw_layer_delete_waypoint_by_name
4743 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4745 wpu_udata *user_data = udata;
4746 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4747 user_data->uuid = id;
4754 * Delete a waypoint by the given name
4755 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4756 * as there be multiple waypoints with the same name
4758 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4761 // Fake a waypoint with the given name
4762 udata.wp = vik_waypoint_new ();
4763 vik_waypoint_set_name (udata.wp, name);
4764 // Currently only the name is used in this waypoint find function
4767 // Hmmm, want key of it
4768 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4770 vik_waypoint_free (udata.wp);
4772 if ( wpf && udata.uuid )
4773 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4779 VikTrack *trk; // input
4780 gpointer uuid; // output
4783 // Only for temporary use by trw_layer_delete_track_by_name
4784 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4786 tpu_udata *user_data = udata;
4787 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4788 user_data->uuid = id;
4795 * Delete a track by the given name
4796 * NOTE: ATM this will delete the first encountered Track with the specified name
4797 * as there may be multiple tracks with the same name within the specified hash table
4799 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4802 // Fake a track with the given name
4803 udata.trk = vik_track_new ();
4804 vik_track_set_name (udata.trk, name);
4805 // Currently only the name is used in this waypoint find function
4808 // Hmmm, want key of it
4809 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4811 vik_track_free (udata.trk);
4813 if ( trkf && udata.uuid ) {
4814 // This could be a little better written...
4815 if ( vtl->tracks == ht_tracks )
4816 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4817 if ( vtl->routes == ht_tracks )
4818 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4825 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4827 vik_treeview_item_delete (vt, it );
4830 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4833 vtl->current_track = NULL;
4834 vtl->route_finder_added_track = NULL;
4835 if (vtl->current_tp_track)
4836 trw_layer_cancel_current_tp(vtl, FALSE);
4838 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4839 g_hash_table_remove_all(vtl->routes_iters);
4840 g_hash_table_remove_all(vtl->routes);
4842 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4844 vik_layer_emit_update ( VIK_LAYER(vtl) );
4847 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4850 vtl->current_track = NULL;
4851 vtl->route_finder_added_track = NULL;
4852 if (vtl->current_tp_track)
4853 trw_layer_cancel_current_tp(vtl, FALSE);
4855 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4856 g_hash_table_remove_all(vtl->tracks_iters);
4857 g_hash_table_remove_all(vtl->tracks);
4859 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4861 vik_layer_emit_update ( VIK_LAYER(vtl) );
4864 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4866 vtl->current_wp = NULL;
4867 vtl->current_wp_id = NULL;
4868 vtl->moving_wp = FALSE;
4870 highest_wp_number_reset(vtl);
4872 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4873 g_hash_table_remove_all(vtl->waypoints_iters);
4874 g_hash_table_remove_all(vtl->waypoints);
4876 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4878 vik_layer_emit_update ( VIK_LAYER(vtl) );
4881 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4883 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4884 // Get confirmation from the user
4885 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4886 _("Are you sure you want to delete all tracks in %s?"),
4887 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4888 vik_trw_layer_delete_all_tracks (vtl);
4891 static void trw_layer_delete_all_routes ( menu_array_layer values )
4893 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4894 // Get confirmation from the user
4895 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4896 _("Are you sure you want to delete all routes in %s?"),
4897 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4898 vik_trw_layer_delete_all_routes (vtl);
4901 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4903 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4904 // Get confirmation from the user
4905 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4906 _("Are you sure you want to delete all waypoints in %s?"),
4907 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4908 vik_trw_layer_delete_all_waypoints (vtl);
4911 static void trw_layer_delete_item ( menu_array_sublayer values )
4913 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4914 gboolean was_visible = FALSE;
4915 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4917 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4918 if ( wp && wp->name ) {
4919 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4920 // Get confirmation from the user
4921 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4922 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4923 _("Are you sure you want to delete the waypoint \"%s\"?"),
4926 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4929 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4931 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4932 if ( trk && trk->name ) {
4933 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4934 // Get confirmation from the user
4935 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4936 _("Are you sure you want to delete the track \"%s\"?"),
4939 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4944 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4945 if ( trk && trk->name ) {
4946 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4947 // Get confirmation from the user
4948 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4949 _("Are you sure you want to delete the route \"%s\"?"),
4952 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4956 vik_layer_emit_update ( VIK_LAYER(vtl) );
4960 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4962 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4964 vik_waypoint_set_name ( wp, new_name );
4966 // Now update the treeview as well
4971 // Need key of it for treeview update
4972 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4974 if ( wpf && udataU.uuid ) {
4975 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4978 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4979 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4985 * Maintain icon of waypoint in the treeview
4987 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4989 // update the treeview
4994 // Need key of it for treeview update
4995 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4997 if ( wpf && udataU.uuid ) {
4998 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5001 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5006 static void trw_layer_properties_item ( menu_array_sublayer values )
5008 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5009 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5011 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5013 if ( wp && wp->name )
5015 gboolean updated = FALSE;
5016 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5018 trw_layer_waypoint_rename ( vtl, wp, new_name );
5020 if ( updated && values[MA_TV_ITER] )
5021 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5023 if ( updated && VIK_LAYER(vtl)->visible )
5024 vik_layer_emit_update ( VIK_LAYER(vtl) );
5030 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5031 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5033 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5035 if ( tr && tr->name )
5037 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5048 * trw_layer_track_statistics:
5050 * Show track statistics.
5051 * ATM jump to the stats page in the properties
5052 * TODO: consider separating the stats into an individual dialog?
5054 static void trw_layer_track_statistics ( menu_array_sublayer values )
5056 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5058 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5059 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5061 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5063 if ( trk && trk->name ) {
5064 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5074 * Update the treeview of the track id - primarily to update the icon
5076 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5082 gpointer *trkf = NULL;
5083 if ( trk->is_route )
5084 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5086 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5088 if ( trkf && udata.uuid ) {
5090 GtkTreeIter *iter = NULL;
5091 if ( trk->is_route )
5092 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5094 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5097 // TODO: Make this a function
5098 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5099 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5100 ((trk->color.green & 0xff00) << 8) |
5101 (trk->color.blue & 0xff00);
5102 gdk_pixbuf_fill ( pixbuf, pixel );
5103 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5104 g_object_unref (pixbuf);
5111 Parameter 1 -> VikLayersPanel
5112 Parameter 2 -> VikLayer
5113 Parameter 3 -> VikViewport
5115 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5118 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5119 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5122 /* since vlp not set, vl & vvp should be valid instead! */
5124 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5125 vik_layer_emit_update ( VIK_LAYER(vl) );
5130 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5132 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5134 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5135 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5137 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5139 if ( track && track->trackpoints )
5140 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5143 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5145 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5147 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5148 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5150 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5152 if ( track && track->trackpoints )
5154 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5156 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5157 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5158 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5159 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5160 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5164 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5166 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5168 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5169 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5171 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5176 // Converting a track to a route can be a bit more complicated,
5177 // so give a chance to change our minds:
5178 if ( !trk->is_route &&
5179 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5180 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5182 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5183 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5188 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5191 trk_copy->is_route = !trk_copy->is_route;
5193 // ATM can't set name to self - so must create temporary copy
5194 gchar *name = g_strdup ( trk_copy->name );
5196 // Delete old one and then add new one
5197 if ( trk->is_route ) {
5198 vik_trw_layer_delete_route ( vtl, trk );
5199 vik_trw_layer_add_track ( vtl, name, trk_copy );
5202 // Extra route conversion bits...
5203 vik_track_merge_segments ( trk_copy );
5204 vik_track_to_routepoints ( trk_copy );
5206 vik_trw_layer_delete_track ( vtl, trk );
5207 vik_trw_layer_add_route ( vtl, name, trk_copy );
5211 // Update in case color of track / route changes when moving between sublayers
5212 vik_layer_emit_update ( VIK_LAYER(vtl) );
5215 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5217 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5219 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5220 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5222 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5225 vik_track_anonymize_times ( track );
5228 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5230 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5232 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5233 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5235 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5240 vtl->current_track = track;
5241 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);
5243 if ( track->trackpoints )
5244 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5248 * extend a track using route finder
5250 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5252 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5253 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5257 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5258 vtl->current_track = track;
5259 vtl->route_finder_started = TRUE;
5261 if ( track->trackpoints )
5262 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5268 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5270 // If have a vlp then perform a basic test to see if any DEM info available...
5272 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5274 if ( !g_list_length(dems) ) {
5275 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5283 * apply_dem_data_common:
5285 * A common function for applying the DEM values and reporting the results.
5287 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5289 if ( !trw_layer_dem_test ( vtl, vlp ) )
5292 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5293 // Inform user how much was changed
5295 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5296 g_snprintf(str, 64, tmp_str, changed);
5297 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5300 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5302 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5304 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5305 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5307 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5310 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5313 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5315 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5317 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5318 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5320 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5323 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5329 * A common function for applying the elevation smoothing and reporting the results.
5331 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5333 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5334 // Inform user how much was changed
5336 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5337 g_snprintf(str, 64, tmp_str, changed);
5338 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5344 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5346 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5348 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5349 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5351 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5356 smooth_it ( vtl, track, FALSE );
5359 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5361 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5363 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5364 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5366 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5371 smooth_it ( vtl, track, TRUE );
5375 * Commonal helper function
5377 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5380 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5381 g_snprintf(str, 64, tmp_str, changed);
5382 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5385 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5387 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5388 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5390 if ( !trw_layer_dem_test ( vtl, vlp ) )
5394 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5396 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5398 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5402 GHashTableIter iter;
5403 gpointer key, value;
5405 g_hash_table_iter_init ( &iter, vtl->waypoints );
5406 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5407 VikWaypoint *wp = VIK_WAYPOINT(value);
5408 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5411 wp_changed_message ( vtl, changed );
5414 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5416 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5417 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5419 if ( !trw_layer_dem_test ( vtl, vlp ) )
5423 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5425 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5427 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5431 GHashTableIter iter;
5432 gpointer key, value;
5434 g_hash_table_iter_init ( &iter, vtl->waypoints );
5435 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5436 VikWaypoint *wp = VIK_WAYPOINT(value);
5437 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5440 wp_changed_message ( vtl, changed );
5443 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5445 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5447 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5448 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5450 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5454 if ( !track->trackpoints )
5456 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5459 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5461 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5463 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5464 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5466 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5471 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5474 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5477 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5479 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5481 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5482 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5484 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5489 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5492 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5495 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5497 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5499 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5500 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5502 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5507 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5510 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5514 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5516 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5518 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5520 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5521 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5523 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5525 if ( trk && trk->trackpoints )
5527 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5528 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5529 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5530 if ( values[MA_VLP] )
5531 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5533 vik_layer_emit_update ( VIK_LAYER(vtl) );
5538 * Refine the selected track/route with a routing engine.
5539 * The routing engine is selected by the user, when requestiong the job.
5541 static void trw_layer_route_refine ( menu_array_sublayer values )
5543 static gint last_engine = 0;
5544 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5547 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5548 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5550 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5552 if ( trk && trk->trackpoints )
5554 /* Check size of the route */
5555 int nb = vik_track_get_tp_count(trk);
5557 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5558 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5559 GTK_MESSAGE_WARNING,
5560 GTK_BUTTONS_OK_CANCEL,
5561 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5563 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5564 gtk_widget_destroy ( dialog );
5565 if (response != GTK_RESPONSE_OK )
5568 /* Select engine from dialog */
5569 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5570 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5571 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5573 GTK_RESPONSE_REJECT,
5575 GTK_RESPONSE_ACCEPT,
5577 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5578 gtk_widget_show_all(label);
5580 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5582 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5583 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5584 gtk_widget_show_all(combo);
5586 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5588 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5590 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5592 /* Dialog validated: retrieve selected engine and do the job */
5593 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5594 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5597 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5599 /* Force saving track */
5600 /* FIXME: remove or rename this hack */
5601 vtl->route_finder_check_added_track = TRUE;
5604 vik_routing_engine_refine (routing, vtl, trk);
5606 /* FIXME: remove or rename this hack */
5607 if ( vtl->route_finder_added_track )
5608 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5610 vtl->route_finder_added_track = NULL;
5611 vtl->route_finder_check_added_track = FALSE;
5613 vik_layer_emit_update ( VIK_LAYER(vtl) );
5615 /* Restore cursor */
5616 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5618 gtk_widget_destroy ( dialog );
5622 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5624 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5625 trw_layer_tpwin_init ( vtl );
5628 /*************************************
5629 * merge/split by time routines
5630 *************************************/
5632 /* called for each key in track hash table.
5633 * If the current track has the same time stamp type, add it to the result,
5634 * except the one pointed by "exclude".
5635 * set exclude to NULL if there is no exclude to check.
5636 * Note that the result is in reverse (for performance reasons).
5641 gboolean with_timestamps;
5643 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5645 twt_udata *user_data = udata;
5646 VikTrackpoint *p1, *p2;
5647 VikTrack *trk = VIK_TRACK(value);
5648 if (trk == user_data->exclude) {
5652 if (trk->trackpoints) {
5653 p1 = vik_track_get_tp_first(trk);
5654 p2 = vik_track_get_tp_last(trk);
5656 if ( user_data->with_timestamps ) {
5657 if (!p1->has_timestamp || !p2->has_timestamp) {
5662 // Don't add tracks with timestamps when getting non timestamp tracks
5663 if (p1->has_timestamp || p2->has_timestamp) {
5669 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5673 * find_nearby_tracks_by_time:
5675 * Called for each track in track hash table.
5676 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5677 * to the current track, then the current track is added to the list in user_data[0]
5679 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5681 VikTrack *trk = VIK_TRACK(value);
5683 GList **nearby_tracks = ((gpointer *)user_data)[0];
5684 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5686 if ( !orig_trk || !orig_trk->trackpoints )
5690 * detect reasons for not merging, and return
5691 * if no reason is found not to merge, then do it.
5694 twt_udata *udata = user_data;
5695 // Exclude the original track from the compiled list
5696 if (trk == udata->exclude) {
5700 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5701 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5703 if (trk->trackpoints) {
5705 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5706 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5708 if (!p1->has_timestamp || !p2->has_timestamp) {
5709 //g_print("no timestamp\n");
5713 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5714 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5715 if (! (abs(t1 - p2->timestamp) < threshold ||
5717 abs(p1->timestamp - t2) < threshold)
5724 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5727 /* comparison function used to sort tracks; a and b are hash table keys */
5728 /* Not actively used - can be restored if needed
5729 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5731 GHashTable *tracks = user_data;
5734 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5735 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5737 if (t1 < t2) return -1;
5738 if (t1 > t2) return 1;
5743 /* comparison function used to sort trackpoints */
5744 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5746 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5748 if (t1 < t2) return -1;
5749 if (t1 > t2) return 1;
5754 * comparison function which can be used to sort tracks or waypoints by name
5756 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5758 const gchar* namea = (const gchar*) a;
5759 const gchar* nameb = (const gchar*) b;
5760 if ( namea == NULL || nameb == NULL)
5763 // Same sort method as used in the vik_treeview_*_alphabetize functions
5764 return strcmp ( namea, nameb );
5768 * Attempt to merge selected track with other tracks specified by the user
5769 * Tracks to merge with must be of the same 'type' as the selected track -
5770 * either all with timestamps, or all without timestamps
5772 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5774 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5775 GList *other_tracks = NULL;
5776 GHashTable *ght_tracks;
5777 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5778 ght_tracks = vtl->routes;
5780 ght_tracks = vtl->tracks;
5782 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5787 if ( !track->trackpoints )
5791 udata.result = &other_tracks;
5792 udata.exclude = track;
5793 // Allow merging with 'similar' time type time tracks
5794 // i.e. either those times, or those without
5795 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5797 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5798 other_tracks = g_list_reverse(other_tracks);
5800 if ( !other_tracks ) {
5801 if ( udata.with_timestamps )
5802 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5804 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5808 // Sort alphabetically for user presentation
5809 // Convert into list of names for usage with dialog function
5810 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5811 GList *other_tracks_names = NULL;
5812 GList *iter = g_list_first ( other_tracks );
5814 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5815 iter = g_list_next ( iter );
5818 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5820 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5824 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5825 g_list_free(other_tracks);
5826 g_list_free(other_tracks_names);
5831 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5832 VikTrack *merge_track;
5833 if ( track->is_route )
5834 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5836 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5839 vik_track_steal_and_append_trackpoints ( track, merge_track );
5840 if ( track->is_route )
5841 vik_trw_layer_delete_route (vtl, merge_track);
5843 vik_trw_layer_delete_track (vtl, merge_track);
5844 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5847 for (l = merge_list; l != NULL; l = g_list_next(l))
5849 g_list_free(merge_list);
5851 vik_layer_emit_update( VIK_LAYER(vtl) );
5855 // c.f. trw_layer_sorted_track_id_by_name_list
5856 // but don't add the specified track to the list (normally current track)
5857 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5859 twt_udata *user_data = udata;
5862 if (trk == user_data->exclude) {
5866 // Sort named list alphabetically
5867 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5871 * Join - this allows combining 'tracks' and 'track routes'
5872 * i.e. doesn't care about whether tracks have consistent timestamps
5873 * ATM can only append one track at a time to the currently selected track
5875 static void trw_layer_append_track ( menu_array_sublayer values )
5878 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5880 GHashTable *ght_tracks;
5881 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5882 ght_tracks = vtl->routes;
5884 ght_tracks = vtl->tracks;
5886 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5891 GList *other_tracks_names = NULL;
5893 // Sort alphabetically for user presentation
5894 // Convert into list of names for usage with dialog function
5895 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5897 udata.result = &other_tracks_names;
5898 udata.exclude = trk;
5900 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5902 // Note the limit to selecting one track only
5903 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5904 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5905 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5908 trk->is_route ? _("Append Route"): _("Append Track"),
5909 trk->is_route ? _("Select the route to append after the current route") :
5910 _("Select the track to append after the current track") );
5912 g_list_free(other_tracks_names);
5914 // It's a list, but shouldn't contain more than one other track!
5915 if ( append_list ) {
5917 for (l = append_list; l != NULL; l = g_list_next(l)) {
5918 // TODO: at present this uses the first track found by name,
5919 // which with potential multiple same named tracks may not be the one selected...
5920 VikTrack *append_track;
5921 if ( trk->is_route )
5922 append_track = vik_trw_layer_get_route ( vtl, l->data );
5924 append_track = vik_trw_layer_get_track ( vtl, l->data );
5926 if ( append_track ) {
5927 vik_track_steal_and_append_trackpoints ( trk, append_track );
5928 if ( trk->is_route )
5929 vik_trw_layer_delete_route (vtl, append_track);
5931 vik_trw_layer_delete_track (vtl, append_track);
5934 for (l = append_list; l != NULL; l = g_list_next(l))
5936 g_list_free(append_list);
5938 vik_layer_emit_update( VIK_LAYER(vtl) );
5943 * Very similar to trw_layer_append_track for joining
5944 * but this allows selection from the 'other' list
5945 * If a track is selected, then is shows routes and joins the selected one
5946 * If a route is selected, then is shows tracks and joins the selected one
5948 static void trw_layer_append_other ( menu_array_sublayer values )
5951 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5953 GHashTable *ght_mykind, *ght_others;
5954 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5955 ght_mykind = vtl->routes;
5956 ght_others = vtl->tracks;
5959 ght_mykind = vtl->tracks;
5960 ght_others = vtl->routes;
5963 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5968 GList *other_tracks_names = NULL;
5970 // Sort alphabetically for user presentation
5971 // Convert into list of names for usage with dialog function
5972 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5974 udata.result = &other_tracks_names;
5975 udata.exclude = trk;
5977 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5979 // Note the limit to selecting one track only
5980 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5981 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5982 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5985 trk->is_route ? _("Append Track"): _("Append Route"),
5986 trk->is_route ? _("Select the track to append after the current route") :
5987 _("Select the route to append after the current track") );
5989 g_list_free(other_tracks_names);
5991 // It's a list, but shouldn't contain more than one other track!
5992 if ( append_list ) {
5994 for (l = append_list; l != NULL; l = g_list_next(l)) {
5995 // TODO: at present this uses the first track found by name,
5996 // which with potential multiple same named tracks may not be the one selected...
5998 // Get FROM THE OTHER TYPE list
5999 VikTrack *append_track;
6000 if ( trk->is_route )
6001 append_track = vik_trw_layer_get_track ( vtl, l->data );
6003 append_track = vik_trw_layer_get_route ( vtl, l->data );
6005 if ( append_track ) {
6007 if ( !append_track->is_route &&
6008 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6009 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6011 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6012 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6013 vik_track_merge_segments ( append_track );
6014 vik_track_to_routepoints ( append_track );
6021 vik_track_steal_and_append_trackpoints ( trk, append_track );
6023 // Delete copied which is FROM THE OTHER TYPE list
6024 if ( trk->is_route )
6025 vik_trw_layer_delete_track (vtl, append_track);
6027 vik_trw_layer_delete_route (vtl, append_track);
6030 for (l = append_list; l != NULL; l = g_list_next(l))
6032 g_list_free(append_list);
6033 vik_layer_emit_update( VIK_LAYER(vtl) );
6037 /* merge by segments */
6038 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6040 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6041 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6042 guint segments = vik_track_merge_segments ( trk );
6043 // NB currently no need to redraw as segments not actually shown on the display
6044 // However inform the user of what happened:
6046 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6047 g_snprintf(str, 64, tmp_str, segments);
6048 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6051 /* merge by time routine */
6052 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6054 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6058 GList *tracks_with_timestamp = NULL;
6059 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6060 if (orig_trk->trackpoints &&
6061 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6062 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6067 udata.result = &tracks_with_timestamp;
6068 udata.exclude = orig_trk;
6069 udata.with_timestamps = TRUE;
6070 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6071 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6073 if (!tracks_with_timestamp) {
6074 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6077 g_list_free(tracks_with_timestamp);
6079 static guint threshold_in_minutes = 1;
6080 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6081 _("Merge Threshold..."),
6082 _("Merge when time between tracks less than:"),
6083 &threshold_in_minutes)) {
6087 // keep attempting to merge all tracks until no merges within the time specified is possible
6088 gboolean attempt_merge = TRUE;
6089 GList *nearby_tracks = NULL;
6091 static gpointer params[3];
6093 while ( attempt_merge ) {
6095 // Don't try again unless tracks have changed
6096 attempt_merge = FALSE;
6098 trps = orig_trk->trackpoints;
6102 if (nearby_tracks) {
6103 g_list_free(nearby_tracks);
6104 nearby_tracks = NULL;
6107 params[0] = &nearby_tracks;
6108 params[1] = orig_trk;
6109 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6111 /* get a list of adjacent-in-time tracks */
6112 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6115 GList *l = nearby_tracks;
6117 /* remove trackpoints from merged track, delete track */
6118 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6119 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6121 // Tracks have changed, therefore retry again against all the remaining tracks
6122 attempt_merge = TRUE;
6127 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6130 g_list_free(nearby_tracks);
6132 vik_layer_emit_update( VIK_LAYER(vtl) );
6136 * Split a track at the currently selected trackpoint
6138 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6140 if ( !vtl->current_tpl )
6143 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6144 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6146 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6147 GList *newglist = g_list_alloc ();
6148 newglist->prev = NULL;
6149 newglist->next = vtl->current_tpl->next;
6150 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6151 tr->trackpoints = newglist;
6153 vtl->current_tpl->next->prev = newglist; /* end old track here */
6154 vtl->current_tpl->next = NULL;
6156 // Bounds of the selected track changed due to the split
6157 vik_track_calculate_bounds ( vtl->current_tp_track );
6159 vtl->current_tpl = newglist; /* change tp to first of new track. */
6160 vtl->current_tp_track = tr;
6163 vik_trw_layer_add_route ( vtl, name, tr );
6165 vik_trw_layer_add_track ( vtl, name, tr );
6167 // Bounds of the new track created by the split
6168 vik_track_calculate_bounds ( tr );
6174 // Also need id of newly created track
6177 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6179 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6181 if ( trkf && udata.uuid )
6182 vtl->current_tp_id = udata.uuid;
6184 vtl->current_tp_id = NULL;
6186 vik_layer_emit_update(VIK_LAYER(vtl));
6192 /* split by time routine */
6193 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6195 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6196 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6197 GList *trps = track->trackpoints;
6199 GList *newlists = NULL;
6200 GList *newtps = NULL;
6201 static guint thr = 1;
6208 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6209 _("Split Threshold..."),
6210 _("Split when time between trackpoints exceeds:"),
6215 /* iterate through trackpoints, and copy them into new lists without touching original list */
6216 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6220 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6222 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6225 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6226 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6227 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6229 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6234 if (ts - prev_ts > thr*60) {
6235 /* flush accumulated trackpoints into new list */
6236 newlists = g_list_append(newlists, g_list_reverse(newtps));
6240 /* accumulate trackpoint copies in newtps, in reverse order */
6241 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6243 iter = g_list_next(iter);
6246 newlists = g_list_append(newlists, g_list_reverse(newtps));
6249 /* put lists of trackpoints into tracks */
6251 // Only bother updating if the split results in new tracks
6252 if (g_list_length (newlists) > 1) {
6257 tr = vik_track_copy ( track, FALSE );
6258 tr->trackpoints = (GList *)(iter->data);
6260 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6261 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6262 g_free ( new_tr_name );
6263 vik_track_calculate_bounds ( tr );
6264 iter = g_list_next(iter);
6266 // Remove original track and then update the display
6267 vik_trw_layer_delete_track (vtl, track);
6268 vik_layer_emit_update(VIK_LAYER(vtl));
6270 g_list_free(newlists);
6274 * Split a track by the number of points as specified by the user
6276 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6278 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6280 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6281 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6283 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6288 // Check valid track
6289 GList *trps = track->trackpoints;
6293 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6294 _("Split Every Nth Point"),
6295 _("Split on every Nth point:"),
6296 250, // Default value as per typical limited track capacity of various GPS devices
6300 // Was a valid number returned?
6306 GList *newlists = NULL;
6307 GList *newtps = NULL;
6312 /* accumulate trackpoint copies in newtps, in reverse order */
6313 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6315 if (count >= points) {
6316 /* flush accumulated trackpoints into new list */
6317 newlists = g_list_append(newlists, g_list_reverse(newtps));
6321 iter = g_list_next(iter);
6324 // If there is a remaining chunk put that into the new split list
6325 // This may well be the whole track if no split points were encountered
6327 newlists = g_list_append(newlists, g_list_reverse(newtps));
6330 /* put lists of trackpoints into tracks */
6332 // Only bother updating if the split results in new tracks
6333 if (g_list_length (newlists) > 1) {
6338 tr = vik_track_copy ( track, FALSE );
6339 tr->trackpoints = (GList *)(iter->data);
6341 if ( track->is_route ) {
6342 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6343 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6346 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6347 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6349 g_free ( new_tr_name );
6350 vik_track_calculate_bounds ( tr );
6352 iter = g_list_next(iter);
6354 // Remove original track and then update the display
6355 if ( track->is_route )
6356 vik_trw_layer_delete_route (vtl, track);
6358 vik_trw_layer_delete_track (vtl, track);
6359 vik_layer_emit_update(VIK_LAYER(vtl));
6361 g_list_free(newlists);
6365 * Split a track at the currently selected trackpoint
6367 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6369 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6370 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6371 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6375 * Split a track by its segments
6376 * Routes do not have segments so don't call this for routes
6378 static void trw_layer_split_segments ( menu_array_sublayer values )
6380 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6381 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6388 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6391 for ( i = 0; i < ntracks; i++ ) {
6393 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6394 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6395 g_free ( new_tr_name );
6400 // Remove original track
6401 vik_trw_layer_delete_track ( vtl, trk );
6402 vik_layer_emit_update ( VIK_LAYER(vtl) );
6405 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6408 /* end of split/merge routines */
6410 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6414 // Find available adjacent trackpoint
6415 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6416 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6417 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6419 // Delete current trackpoint
6420 vik_trackpoint_free ( vtl->current_tpl->data );
6421 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6423 // Set to current to the available adjacent trackpoint
6424 vtl->current_tpl = new_tpl;
6426 if ( vtl->current_tp_track ) {
6427 vik_track_calculate_bounds ( vtl->current_tp_track );
6431 // Delete current trackpoint
6432 vik_trackpoint_free ( vtl->current_tpl->data );
6433 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6434 trw_layer_cancel_current_tp ( vtl, FALSE );
6439 * Delete the selected point
6441 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6443 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6445 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6446 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6448 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6453 if ( !vtl->current_tpl )
6456 trw_layer_trackpoint_selected_delete ( vtl, trk );
6458 // Track has been updated so update tps:
6459 trw_layer_cancel_tps_of_track ( vtl, trk );
6461 vik_layer_emit_update ( VIK_LAYER(vtl) );
6465 * Delete adjacent track points at the same position
6466 * AKA Delete Dulplicates on the Properties Window
6468 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6470 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6472 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6473 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6475 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6480 gulong removed = vik_track_remove_dup_points ( trk );
6482 // Track has been updated so update tps:
6483 trw_layer_cancel_tps_of_track ( vtl, trk );
6485 // Inform user how much was deleted as it's not obvious from the normal view
6487 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6488 g_snprintf(str, 64, tmp_str, removed);
6489 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6491 vik_layer_emit_update ( VIK_LAYER(vtl) );
6495 * Delete adjacent track points with the same timestamp
6496 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6498 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6500 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6502 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6503 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6505 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6510 gulong removed = vik_track_remove_same_time_points ( trk );
6512 // Track has been updated so update tps:
6513 trw_layer_cancel_tps_of_track ( vtl, trk );
6515 // Inform user how much was deleted as it's not obvious from the normal view
6517 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6518 g_snprintf(str, 64, tmp_str, removed);
6519 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6521 vik_layer_emit_update ( VIK_LAYER(vtl) );
6527 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6529 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6531 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6532 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6534 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6539 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6541 vik_layer_emit_update ( VIK_LAYER(vtl) );
6544 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6546 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6548 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6549 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6551 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6556 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6558 vik_layer_emit_update ( VIK_LAYER(vtl) );
6564 static void trw_layer_reverse ( menu_array_sublayer values )
6566 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6568 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6569 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6571 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6576 vik_track_reverse ( track );
6578 vik_layer_emit_update ( VIK_LAYER(vtl) );
6582 * Open a diary at the specified date
6584 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6587 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6588 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6589 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6590 g_error_free ( err );
6596 * Open a diary at the date of the track or waypoint
6598 static void trw_layer_diary ( menu_array_sublayer values )
6600 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6602 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6603 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6609 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6610 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6611 trw_layer_diary_open ( vtl, date_buf );
6614 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6616 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6617 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6623 if ( wpt->has_timestamp ) {
6624 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6625 trw_layer_diary_open ( vtl, date_buf );
6628 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6633 * Similar to trw_layer_enum_item, but this uses a sorted method
6636 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6638 GList **list = (GList**)udata;
6639 // *list = g_list_prepend(*all, key); //unsorted method
6640 // Sort named list alphabetically
6641 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6646 * Now Waypoint specific sort
6648 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6650 GList **list = (GList**)udata;
6651 // Sort named list alphabetically
6652 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6656 * Track specific sort
6658 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6660 GList **list = (GList**)udata;
6661 // Sort named list alphabetically
6662 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6667 gboolean has_same_track_name;
6668 const gchar *same_track_name;
6669 } same_track_name_udata;
6671 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6673 const gchar* namea = (const gchar*) aa;
6674 const gchar* nameb = (const gchar*) bb;
6677 gint result = strcmp ( namea, nameb );
6679 if ( result == 0 ) {
6680 // Found two names the same
6681 same_track_name_udata *user_data = udata;
6682 user_data->has_same_track_name = TRUE;
6683 user_data->same_track_name = namea;
6686 // Leave ordering the same
6691 * Find out if any tracks have the same name in this hash table
6693 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6695 // Sort items by name, then compare if any next to each other are the same
6697 GList *track_names = NULL;
6698 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6701 if ( ! track_names )
6704 same_track_name_udata udata;
6705 udata.has_same_track_name = FALSE;
6707 // Use sort routine to traverse list comparing items
6708 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6709 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6710 // Still no tracks...
6714 return udata.has_same_track_name;
6718 * Force unqiue track names for the track table specified
6719 * Note the panel is a required parameter to enable the update of the names displayed
6720 * Specify if on tracks or else on routes
6722 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6724 // . Search list for an instance of repeated name
6725 // . get track of this name
6726 // . create new name
6727 // . rename track & update equiv. treeview iter
6728 // . repeat until all different
6730 same_track_name_udata udata;
6732 GList *track_names = NULL;
6733 udata.has_same_track_name = FALSE;
6734 udata.same_track_name = NULL;
6736 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6739 if ( ! track_names )
6742 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6744 // Still no tracks...
6745 if ( ! dummy_list1 )
6748 while ( udata.has_same_track_name ) {
6750 // Find a track with the same name
6753 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6755 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6759 g_critical("Houston, we've had a problem.");
6760 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6761 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6766 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6767 vik_track_set_name ( trk, newname );
6773 // Need want key of it for treeview update
6774 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6776 if ( trkf && udataU.uuid ) {
6780 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6782 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6785 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6787 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6789 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6793 // Start trying to find same names again...
6795 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6796 udata.has_same_track_name = FALSE;
6797 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6799 // No tracks any more - give up searching
6800 if ( ! dummy_list2 )
6801 udata.has_same_track_name = FALSE;
6805 vik_layers_panel_emit_update ( vlp );
6808 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6810 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6813 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6814 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6815 iter = &(vtl->tracks_iter);
6816 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6818 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6819 iter = &(vtl->routes_iter);
6820 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6822 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6823 iter = &(vtl->waypoints_iter);
6824 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6828 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6831 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6833 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6836 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6837 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6838 iter = &(vtl->tracks_iter);
6839 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6841 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6842 iter = &(vtl->routes_iter);
6843 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6845 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6846 iter = &(vtl->waypoints_iter);
6847 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6851 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6857 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6859 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6862 // Ensure list of track names offered is unique
6863 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6864 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6865 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6866 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6872 // Sort list alphabetically for better presentation
6873 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6876 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6880 // Get list of items to delete from the user
6881 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6884 _("Delete Selection"),
6885 _("Select tracks to delete"));
6888 // Delete requested tracks
6889 // since specificly requested, IMHO no need for extra confirmation
6890 if ( delete_list ) {
6892 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6893 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6894 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6896 g_list_free(delete_list);
6897 vik_layer_emit_update( VIK_LAYER(vtl) );
6904 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6906 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6909 // Ensure list of track names offered is unique
6910 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6911 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6912 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6913 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6919 // Sort list alphabetically for better presentation
6920 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6923 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6927 // Get list of items to delete from the user
6928 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6931 _("Delete Selection"),
6932 _("Select routes to delete") );
6935 // Delete requested routes
6936 // since specificly requested, IMHO no need for extra confirmation
6937 if ( delete_list ) {
6939 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6940 // This deletes first route it finds of that name (but uniqueness is enforced above)
6941 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6943 g_list_free(delete_list);
6944 vik_layer_emit_update( VIK_LAYER(vtl) );
6949 gboolean has_same_waypoint_name;
6950 const gchar *same_waypoint_name;
6951 } same_waypoint_name_udata;
6953 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6955 const gchar* namea = (const gchar*) aa;
6956 const gchar* nameb = (const gchar*) bb;
6959 gint result = strcmp ( namea, nameb );
6961 if ( result == 0 ) {
6962 // Found two names the same
6963 same_waypoint_name_udata *user_data = udata;
6964 user_data->has_same_waypoint_name = TRUE;
6965 user_data->same_waypoint_name = namea;
6968 // Leave ordering the same
6973 * Find out if any waypoints have the same name in this layer
6975 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6977 // Sort items by name, then compare if any next to each other are the same
6979 GList *waypoint_names = NULL;
6980 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6983 if ( ! waypoint_names )
6986 same_waypoint_name_udata udata;
6987 udata.has_same_waypoint_name = FALSE;
6989 // Use sort routine to traverse list comparing items
6990 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6991 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6992 // Still no waypoints...
6996 return udata.has_same_waypoint_name;
7000 * Force unqiue waypoint names for this layer
7001 * Note the panel is a required parameter to enable the update of the names displayed
7003 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7005 // . Search list for an instance of repeated name
7006 // . get waypoint of this name
7007 // . create new name
7008 // . rename waypoint & update equiv. treeview iter
7009 // . repeat until all different
7011 same_waypoint_name_udata udata;
7013 GList *waypoint_names = NULL;
7014 udata.has_same_waypoint_name = FALSE;
7015 udata.same_waypoint_name = NULL;
7017 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7020 if ( ! waypoint_names )
7023 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7025 // Still no waypoints...
7026 if ( ! dummy_list1 )
7029 while ( udata.has_same_waypoint_name ) {
7031 // Find a waypoint with the same name
7032 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7036 g_critical("Houston, we've had a problem.");
7037 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7038 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7043 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7045 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7047 // Start trying to find same names again...
7048 waypoint_names = NULL;
7049 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7050 udata.has_same_waypoint_name = FALSE;
7051 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7053 // No waypoints any more - give up searching
7054 if ( ! dummy_list2 )
7055 udata.has_same_waypoint_name = FALSE;
7059 vik_layers_panel_emit_update ( vlp );
7065 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7067 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7070 // Ensure list of waypoint names offered is unique
7071 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7072 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7073 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7074 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7080 // Sort list alphabetically for better presentation
7081 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7083 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7087 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7089 // Get list of items to delete from the user
7090 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7093 _("Delete Selection"),
7094 _("Select waypoints to delete"));
7097 // Delete requested waypoints
7098 // since specificly requested, IMHO no need for extra confirmation
7099 if ( delete_list ) {
7101 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7102 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7103 trw_layer_delete_waypoint_by_name (vtl, l->data);
7105 g_list_free(delete_list);
7107 trw_layer_calculate_bounds_waypoints ( vtl );
7108 vik_layer_emit_update( VIK_LAYER(vtl) );
7116 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7118 vik_treeview_item_toggle_visible ( vt, it );
7124 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7126 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7132 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7134 wp->visible = GPOINTER_TO_INT (on_off);
7140 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7142 wp->visible = !wp->visible;
7148 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7150 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7151 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7152 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7153 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7155 vik_layer_emit_update ( VIK_LAYER(vtl) );
7161 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7163 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7164 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7165 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7166 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7168 vik_layer_emit_update ( VIK_LAYER(vtl) );
7174 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7176 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7177 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7178 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7180 vik_layer_emit_update ( VIK_LAYER(vtl) );
7186 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7188 trk->visible = GPOINTER_TO_INT (on_off);
7194 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7196 trk->visible = !trk->visible;
7202 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7204 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7205 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7206 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7207 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7209 vik_layer_emit_update ( VIK_LAYER(vtl) );
7215 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7217 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7218 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7219 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7220 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7222 vik_layer_emit_update ( VIK_LAYER(vtl) );
7228 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7230 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7231 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7232 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7234 vik_layer_emit_update ( VIK_LAYER(vtl) );
7240 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7242 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7243 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7244 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7245 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7247 vik_layer_emit_update ( VIK_LAYER(vtl) );
7253 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7255 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7256 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7257 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7258 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7260 vik_layer_emit_update ( VIK_LAYER(vtl) );
7266 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7268 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7269 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7270 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7272 vik_layer_emit_update ( VIK_LAYER(vtl) );
7276 * vik_trw_layer_build_waypoint_list_t:
7278 * Helper function to construct a list of #vik_trw_waypoint_list_t
7280 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7282 GList *waypoints_and_layers = NULL;
7283 // build waypoints_and_layers list
7284 while ( waypoints ) {
7285 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7286 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7288 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7289 waypoints = g_list_next ( waypoints );
7291 return waypoints_and_layers;
7295 * trw_layer_create_waypoint_list:
7297 * Create the latest list of waypoints with the associated layer(s)
7298 * Although this will always be from a single layer here
7300 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7302 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7303 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7305 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7309 * trw_layer_analyse_close:
7311 * Stuff to do on dialog closure
7313 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7315 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7316 gtk_widget_destroy ( dialog );
7317 vtl->tracks_analysis_dialog = NULL;
7321 * vik_trw_layer_build_track_list_t:
7323 * Helper function to construct a list of #vik_trw_track_list_t
7325 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7327 GList *tracks_and_layers = NULL;
7328 // build tracks_and_layers list
7330 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7331 vtdl->trk = VIK_TRACK(tracks->data);
7333 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7334 tracks = g_list_next ( tracks );
7336 return tracks_and_layers;
7340 * trw_layer_create_track_list:
7342 * Create the latest list of tracks with the associated layer(s)
7343 * Although this will always be from a single layer here
7345 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7347 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7348 GList *tracks = NULL;
7349 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7350 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7352 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7354 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7357 static void trw_layer_tracks_stats ( menu_array_layer values )
7359 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7360 // There can only be one!
7361 if ( vtl->tracks_analysis_dialog )
7364 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7365 VIK_LAYER(vtl)->name,
7367 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7368 trw_layer_create_track_list,
7369 trw_layer_analyse_close );
7375 static void trw_layer_routes_stats ( menu_array_layer values )
7377 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7378 // There can only be one!
7379 if ( vtl->tracks_analysis_dialog )
7382 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7383 VIK_LAYER(vtl)->name,
7385 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7386 trw_layer_create_track_list,
7387 trw_layer_analyse_close );
7390 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7392 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7393 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7395 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7398 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7400 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7401 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7404 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7405 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7409 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7411 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7412 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7416 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7417 } else if ( !strncmp(wp->comment, "http", 4) ) {
7418 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7419 } else if ( !strncmp(wp->description, "http", 4) ) {
7420 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7424 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7426 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7428 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7430 // No actual change to the name supplied
7432 if (strcmp(newname, wp->name) == 0 )
7435 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7438 // An existing waypoint has been found with the requested name
7439 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7440 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7445 // Update WP name and refresh the treeview
7446 vik_waypoint_set_name (wp, newname);
7448 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7449 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7451 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7456 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7458 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7460 // No actual change to the name supplied
7462 if (strcmp(newname, trk->name) == 0)
7465 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7468 // An existing track has been found with the requested name
7469 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7470 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7474 // Update track name and refresh GUI parts
7475 vik_track_set_name (trk, newname);
7477 // Update any subwindows that could be displaying this track which has changed name
7478 // Only one Track Edit Window
7479 if ( l->current_tp_track == trk && l->tpwin ) {
7480 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7482 // Property Dialog of the track
7483 vik_trw_layer_propwin_update ( trk );
7485 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7486 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7488 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7493 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7495 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7497 // No actual change to the name supplied
7499 if (strcmp(newname, trk->name) == 0)
7502 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7505 // An existing track has been found with the requested name
7506 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7507 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7511 // Update track name and refresh GUI parts
7512 vik_track_set_name (trk, newname);
7514 // Update any subwindows that could be displaying this track which has changed name
7515 // Only one Track Edit Window
7516 if ( l->current_tp_track == trk && l->tpwin ) {
7517 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7519 // Property Dialog of the track
7520 vik_trw_layer_propwin_update ( trk );
7522 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7523 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7525 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7532 static gboolean is_valid_geocache_name ( gchar *str )
7534 gint len = strlen ( str );
7535 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]));
7538 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7540 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7541 a_acquire_set_filter_track ( trk );
7544 #ifdef VIK_CONFIG_GOOGLE
7545 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7547 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7548 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7551 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7553 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7555 gchar *escaped = uri_escape ( tr->comment );
7556 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7557 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7564 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7565 /* viewpoint is now available instead */
7566 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7568 static menu_array_sublayer pass_along;
7570 gboolean rv = FALSE;
7572 pass_along[MA_VTL] = l;
7573 pass_along[MA_VLP] = vlp;
7574 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7575 pass_along[MA_SUBLAYER_ID] = sublayer;
7576 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7577 pass_along[MA_VVP] = vvp;
7578 pass_along[MA_TV_ITER] = iter;
7579 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7581 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7585 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7586 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7587 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7588 gtk_widget_show ( item );
7590 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7591 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7592 if (tr && tr->property_dialog)
7593 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7595 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7596 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7597 if (tr && tr->property_dialog)
7598 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7601 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7602 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7603 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7604 gtk_widget_show ( item );
7606 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7607 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7608 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7609 gtk_widget_show ( item );
7611 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7613 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7614 gtk_widget_show ( item );
7616 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7618 // Always create separator as now there is always at least the transform menu option
7619 item = gtk_menu_item_new ();
7620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7621 gtk_widget_show ( item );
7623 /* could be a right-click using the tool */
7624 if ( vlp != NULL ) {
7625 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7626 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7628 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7629 gtk_widget_show ( item );
7632 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7634 if ( wp && wp->name ) {
7635 if ( is_valid_geocache_name ( wp->name ) ) {
7636 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7638 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7639 gtk_widget_show ( item );
7641 #ifdef VIK_CONFIG_GEOTAG
7642 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7643 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7644 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7645 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7646 gtk_widget_show ( item );
7650 if ( wp && wp->image )
7652 // Set up image paramater
7653 pass_along[MA_MISC] = wp->image;
7655 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7656 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
7657 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7658 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7659 gtk_widget_show ( item );
7661 #ifdef VIK_CONFIG_GEOTAG
7662 GtkWidget *geotag_submenu = gtk_menu_new ();
7663 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7664 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7665 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7666 gtk_widget_show ( item );
7667 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7669 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7670 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7671 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7672 gtk_widget_show ( item );
7674 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7675 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7676 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7677 gtk_widget_show ( item );
7684 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7685 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7686 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7687 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7688 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7689 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7690 gtk_widget_show ( item );
7696 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7697 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7699 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7700 gtk_widget_show ( item );
7701 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7702 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7703 gtk_widget_set_sensitive ( item, TRUE );
7705 gtk_widget_set_sensitive ( item, FALSE );
7708 item = gtk_menu_item_new ();
7709 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7710 gtk_widget_show ( item );
7713 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7716 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7717 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7718 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720 gtk_widget_show ( item );
7723 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7725 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7726 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7727 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7728 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7729 gtk_widget_show ( item );
7731 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7732 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7734 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7735 gtk_widget_show ( item );
7737 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7738 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7739 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7740 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7741 gtk_widget_show ( item );
7743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7746 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7747 gtk_widget_show ( item );
7749 GtkWidget *vis_submenu = gtk_menu_new ();
7750 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7751 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7752 gtk_widget_show ( item );
7753 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7755 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7756 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7759 gtk_widget_show ( item );
7761 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7764 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7765 gtk_widget_show ( item );
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7770 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7771 gtk_widget_show ( item );
7773 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7776 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7779 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7783 if ( l->current_track && !l->current_track->is_route ) {
7784 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7786 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7787 gtk_widget_show ( item );
7789 item = gtk_menu_item_new ();
7790 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7791 gtk_widget_show ( item );
7794 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7797 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7798 gtk_widget_show ( item );
7800 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7803 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7804 gtk_widget_show ( item );
7805 // Make it available only when a new track *not* already in progress
7806 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7808 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7812 gtk_widget_show ( item );
7814 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7817 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7818 gtk_widget_show ( item );
7820 GtkWidget *vis_submenu = gtk_menu_new ();
7821 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7822 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7823 gtk_widget_show ( item );
7824 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7826 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7827 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7828 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7829 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7830 gtk_widget_show ( item );
7832 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7833 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7834 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7835 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7836 gtk_widget_show ( item );
7838 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7839 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7840 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7841 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7843 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7844 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7846 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7847 gtk_widget_show ( item );
7849 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7850 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7851 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7852 gtk_widget_show ( item );
7855 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7859 if ( l->current_track && l->current_track->is_route ) {
7860 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7861 // Reuse finish track method
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7863 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7864 gtk_widget_show ( item );
7866 item = gtk_menu_item_new ();
7867 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7868 gtk_widget_show ( item );
7871 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7872 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7874 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7875 gtk_widget_show ( item );
7877 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7878 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7880 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7881 gtk_widget_show ( item );
7882 // Make it available only when a new track *not* already in progress
7883 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7885 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7888 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7889 gtk_widget_show ( item );
7891 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7892 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7894 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7895 gtk_widget_show ( item );
7897 GtkWidget *vis_submenu = gtk_menu_new ();
7898 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7899 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7900 gtk_widget_show ( item );
7901 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7903 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7904 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7905 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7906 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7907 gtk_widget_show ( item );
7909 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7910 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7911 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7912 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7913 gtk_widget_show ( item );
7915 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7916 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7917 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7918 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7920 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7921 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7923 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7925 gtk_widget_show ( item );
7927 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7930 gtk_widget_show ( item );
7934 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7935 GtkWidget *submenu_sort = gtk_menu_new ();
7936 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7937 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7938 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7939 gtk_widget_show ( item );
7940 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7942 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7943 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7945 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7946 gtk_widget_show ( item );
7948 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7949 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7951 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7952 gtk_widget_show ( item );
7955 GtkWidget *upload_submenu = gtk_menu_new ();
7957 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7959 item = gtk_menu_item_new ();
7960 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7961 gtk_widget_show ( item );
7963 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7964 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7965 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7966 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7967 if ( l->current_track ) {
7968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7969 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7970 gtk_widget_show ( item );
7973 item = gtk_menu_item_new ();
7974 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7975 gtk_widget_show ( item );
7978 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7979 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7981 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7982 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7984 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7985 gtk_widget_show ( item );
7987 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7989 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7990 gtk_widget_show ( item );
7992 GtkWidget *goto_submenu;
7993 goto_submenu = gtk_menu_new ();
7994 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7995 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7996 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7997 gtk_widget_show ( item );
7998 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8000 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8003 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8004 gtk_widget_show ( item );
8006 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8007 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8009 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8010 gtk_widget_show ( item );
8012 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8013 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8015 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8016 gtk_widget_show ( item );
8018 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8019 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8021 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8022 gtk_widget_show ( item );
8024 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8025 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8027 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8028 gtk_widget_show ( item );
8030 // Routes don't have speeds
8031 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8032 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8035 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8036 gtk_widget_show ( item );
8039 GtkWidget *combine_submenu;
8040 combine_submenu = gtk_menu_new ();
8041 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8042 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8043 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8044 gtk_widget_show ( item );
8045 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8047 // Routes don't have times or segments...
8048 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8049 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8051 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8052 gtk_widget_show ( item );
8054 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8055 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8056 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8057 gtk_widget_show ( item );
8060 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8062 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8063 gtk_widget_show ( item );
8065 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8066 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8068 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8069 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8070 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8071 gtk_widget_show ( item );
8073 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8074 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8076 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8078 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8079 gtk_widget_show ( item );
8081 GtkWidget *split_submenu;
8082 split_submenu = gtk_menu_new ();
8083 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8085 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8086 gtk_widget_show ( item );
8087 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8089 // Routes don't have times or segments...
8090 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8091 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8092 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8093 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8094 gtk_widget_show ( item );
8096 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8097 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8099 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8100 gtk_widget_show ( item );
8103 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8105 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8106 gtk_widget_show ( item );
8108 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8110 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8111 gtk_widget_show ( item );
8112 // Make it available only when a trackpoint is selected.
8113 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8115 GtkWidget *insert_submenu = gtk_menu_new ();
8116 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8117 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8118 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8119 gtk_widget_show ( item );
8120 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8122 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8124 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8125 gtk_widget_show ( item );
8126 // Make it available only when a point is selected
8127 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8129 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8131 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8132 gtk_widget_show ( item );
8133 // Make it available only when a point is selected
8134 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8136 GtkWidget *delete_submenu;
8137 delete_submenu = gtk_menu_new ();
8138 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8140 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8141 gtk_widget_show ( item );
8142 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8144 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8145 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8147 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8148 gtk_widget_show ( item );
8149 // Make it available only when a point is selected
8150 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8152 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8154 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8155 gtk_widget_show ( item );
8157 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8159 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8160 gtk_widget_show ( item );
8162 GtkWidget *transform_submenu;
8163 transform_submenu = gtk_menu_new ();
8164 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8165 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8166 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8167 gtk_widget_show ( item );
8168 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8170 GtkWidget *dem_submenu;
8171 dem_submenu = gtk_menu_new ();
8172 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8173 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
8174 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8175 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8177 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8179 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8180 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8181 gtk_widget_show ( item );
8183 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8185 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8186 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8187 gtk_widget_show ( item );
8189 GtkWidget *smooth_submenu;
8190 smooth_submenu = gtk_menu_new ();
8191 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8192 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8193 gtk_widget_show ( item );
8194 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8196 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8197 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8198 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8199 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8200 gtk_widget_show ( item );
8202 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8204 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8205 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8206 gtk_widget_show ( item );
8208 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8209 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8211 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8212 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8214 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8215 gtk_widget_show ( item );
8217 // Routes don't have timestamps - so this is only available for tracks
8218 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8221 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8222 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8223 gtk_widget_show ( item );
8226 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8227 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8229 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8230 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8231 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8232 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8233 gtk_widget_show ( item );
8235 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8236 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8237 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8238 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8239 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8240 gtk_widget_show ( item );
8243 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8245 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8246 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8248 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8249 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
8250 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8251 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8252 gtk_widget_show ( item );
8255 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8256 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8258 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8262 gtk_widget_show ( item );
8264 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8265 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8267 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8268 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8269 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8270 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8271 gtk_widget_show ( item );
8273 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8274 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8275 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
8276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8277 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8278 gtk_widget_show ( item );
8281 // ATM can't upload a single waypoint but can do waypoints to a GPS
8282 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8283 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8284 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8285 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8286 gtk_widget_show ( item );
8287 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8289 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8290 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8292 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8293 gtk_widget_show ( item );
8297 // Only made available if a suitable program is installed
8298 if ( have_diary_program ) {
8299 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8300 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8301 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8302 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8303 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8304 gtk_widget_show ( item );
8308 #ifdef VIK_CONFIG_GOOGLE
8309 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8311 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8312 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8314 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8315 gtk_widget_show ( item );
8319 // Some things aren't usable with routes
8320 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8321 #ifdef VIK_CONFIG_OPENSTREETMAP
8322 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8323 // Convert internal pointer into track
8324 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8325 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8326 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8327 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8328 gtk_widget_show ( item );
8331 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8332 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8333 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8334 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8335 gtk_widget_show ( item );
8337 /* ATM This function is only available via the layers panel, due to needing a vlp */
8339 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8340 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8341 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8343 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8344 gtk_widget_show ( item );
8348 #ifdef VIK_CONFIG_GEOTAG
8349 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8350 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8351 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8352 gtk_widget_show ( item );
8356 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8357 // Only show on viewport popmenu when a trackpoint is selected
8358 if ( ! vlp && l->current_tpl ) {
8360 item = gtk_menu_item_new ();
8361 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8362 gtk_widget_show ( item );
8364 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8365 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8367 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8368 gtk_widget_show ( item );
8372 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8373 GtkWidget *transform_submenu;
8374 transform_submenu = gtk_menu_new ();
8375 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8376 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8377 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8378 gtk_widget_show ( item );
8379 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8381 GtkWidget *dem_submenu;
8382 dem_submenu = gtk_menu_new ();
8383 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8384 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
8385 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8386 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8388 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8389 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8390 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8391 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8392 gtk_widget_show ( item );
8394 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8395 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8396 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8397 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8398 gtk_widget_show ( item );
8401 gtk_widget_show_all ( GTK_WIDGET(menu) );
8406 // TODO: Probably better to rework this track manipulation in viktrack.c
8407 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8410 if (!vtl->current_tpl)
8413 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8414 VikTrackpoint *tp_other = NULL;
8417 if (!vtl->current_tpl->prev)
8419 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8421 if (!vtl->current_tpl->next)
8423 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8426 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8429 VikTrackpoint *tp_new = vik_trackpoint_new();
8430 struct LatLon ll_current, ll_other;
8431 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8432 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8434 /* main positional interpolation */
8435 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8436 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8438 /* Now other properties that can be interpolated */
8439 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8441 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8442 /* Note here the division is applied to each part, then added
8443 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8444 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8445 tp_new->has_timestamp = TRUE;
8448 if (tp_current->speed != NAN && tp_other->speed != NAN)
8449 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8451 /* TODO - improve interpolation of course, as it may not be correct.
8452 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8453 [similar applies if value is in radians] */
8454 if (tp_current->course != NAN && tp_other->course != NAN)
8455 tp_new->course = (tp_current->course + tp_other->course)/2;
8457 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8459 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8460 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8462 // Otherwise try routes
8463 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8467 gint index = g_list_index ( trk->trackpoints, tp_current );
8471 // NB no recalculation of bounds since it is inserted between points
8472 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8477 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8483 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8487 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8489 if ( vtl->current_tpl )
8491 vtl->current_tpl = NULL;
8492 vtl->current_tp_track = NULL;
8493 vtl->current_tp_id = NULL;
8494 vik_layer_emit_update(VIK_LAYER(vtl));
8498 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8500 g_assert ( vtl->tpwin != NULL );
8501 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8502 trw_layer_cancel_current_tp ( vtl, TRUE );
8504 if ( vtl->current_tpl == NULL )
8507 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8509 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8510 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8512 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8514 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8516 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8520 trw_layer_trackpoint_selected_delete ( vtl, tr );
8522 if ( vtl->current_tpl )
8523 // Reset dialog with the available adjacent trackpoint
8524 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8526 vik_layer_emit_update(VIK_LAYER(vtl));
8528 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8530 if ( vtl->current_tp_track )
8531 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8532 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8534 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8536 if ( vtl->current_tp_track )
8537 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8538 vik_layer_emit_update(VIK_LAYER(vtl));
8540 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8542 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8543 vik_layer_emit_update(VIK_LAYER(vtl));
8545 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8546 vik_layer_emit_update(VIK_LAYER(vtl));
8550 * trw_layer_dialog_shift:
8551 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8553 * Try to reposition a dialog if it's over the specified coord
8554 * so to not obscure the item of interest
8556 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8558 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8560 // Attempt force dialog to be shown so we can find out where it is more reliably...
8561 while ( gtk_events_pending() )
8562 gtk_main_iteration ();
8564 // get parent window position & size
8565 gint win_pos_x, win_pos_y;
8566 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8568 gint win_size_x, win_size_y;
8569 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8571 // get own dialog size
8572 gint dia_size_x, dia_size_y;
8573 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8575 // get own dialog position
8576 gint dia_pos_x, dia_pos_y;
8577 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8579 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8580 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8582 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8584 gint vp_xx, vp_yy; // In viewport pixels
8585 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8587 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8591 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8593 // Transform Viewport pixels into absolute pixels
8594 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8595 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8597 // Is dialog over the point (to within an ^^ edge value)
8598 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8599 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8603 gint hh = vik_viewport_get_height ( vvp );
8605 // Consider the difference in viewport to the full window
8606 gint offset_y = dest_y;
8607 // Add difference between dialog and window sizes
8608 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8610 if ( vp_yy > hh/2 ) {
8611 // Point in bottom half, move window to top half
8612 gtk_window_move ( dialog, dia_pos_x, offset_y );
8615 // Point in top half, move dialog down
8616 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8620 // Shift left<->right
8621 gint ww = vik_viewport_get_width ( vvp );
8623 // Consider the difference in viewport to the full window
8624 gint offset_x = dest_x;
8625 // Add difference between dialog and window sizes
8626 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8628 if ( vp_xx > ww/2 ) {
8629 // Point on right, move window to left
8630 gtk_window_move ( dialog, offset_x, dia_pos_y );
8633 // Point on left, move right
8634 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8642 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8646 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8647 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8648 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8649 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8651 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8653 if ( vtl->current_tpl ) {
8654 // get tp pixel position
8655 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8657 // Shift up<->down to try not to obscure the trackpoint.
8658 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8662 if ( vtl->current_tpl )
8663 if ( vtl->current_tp_track )
8664 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8665 /* set layer name and TP data */
8668 /***************************************************************************
8670 ***************************************************************************/
8672 /*** Utility data structures and functions ****/
8676 gint closest_x, closest_y;
8677 gboolean draw_images;
8678 gpointer *closest_wp_id;
8679 VikWaypoint *closest_wp;
8685 gint closest_x, closest_y;
8686 gpointer closest_track_id;
8687 VikTrackpoint *closest_tp;
8693 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8699 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8701 // If waypoint has an image then use the image size to select
8702 if ( params->draw_images && wp->image ) {
8703 gint slackx, slacky;
8704 slackx = wp->image_width / 2;
8705 slacky = wp->image_height / 2;
8707 if ( x <= params->x + slackx && x >= params->x - slackx
8708 && y <= params->y + slacky && y >= params->y - slacky ) {
8709 params->closest_wp_id = id;
8710 params->closest_wp = wp;
8711 params->closest_x = x;
8712 params->closest_y = y;
8715 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8716 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8717 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8719 params->closest_wp_id = id;
8720 params->closest_wp = wp;
8721 params->closest_x = x;
8722 params->closest_y = y;
8726 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8728 GList *tpl = t->trackpoints;
8734 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8740 tp = VIK_TRACKPOINT(tpl->data);
8742 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8744 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8745 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8746 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8748 params->closest_track_id = id;
8749 params->closest_tp = tp;
8750 params->closest_tpl = tpl;
8751 params->closest_x = x;
8752 params->closest_y = y;
8758 // ATM: Leave this as 'Track' only.
8759 // Not overly bothered about having a snap to route trackpoint capability
8760 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8762 TPSearchParams params;
8766 params.closest_track_id = NULL;
8767 params.closest_tp = NULL;
8768 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8769 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8770 return params.closest_tp;
8773 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8775 WPSearchParams params;
8779 params.draw_images = vtl->drawimages;
8780 params.closest_wp = NULL;
8781 params.closest_wp_id = NULL;
8782 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8783 return params.closest_wp;
8787 // Some forward declarations
8788 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8789 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8790 static void marker_end_move ( tool_ed_t *t );
8793 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8797 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8799 // Here always allow snapping back to the original location
8800 // this is useful when one decides not to move the thing afterall
8801 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8804 if ( event->state & GDK_CONTROL_MASK )
8806 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8808 new_coord = tp->coord;
8812 if ( event->state & GDK_SHIFT_MASK )
8814 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8816 new_coord = wp->coord;
8820 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8822 marker_moveto ( t, x, y );
8829 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8831 if ( t->holding && event->button == 1 )
8834 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8837 if ( event->state & GDK_CONTROL_MASK )
8839 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8841 new_coord = tp->coord;
8845 if ( event->state & GDK_SHIFT_MASK )
8847 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8849 new_coord = wp->coord;
8852 marker_end_move ( t );
8854 // Determine if working on a waypoint or a trackpoint
8855 if ( t->is_waypoint ) {
8856 // Update waypoint position
8857 vtl->current_wp->coord = new_coord;
8858 trw_layer_calculate_bounds_waypoints ( vtl );
8859 // Reset waypoint pointer
8860 vtl->current_wp = NULL;
8861 vtl->current_wp_id = NULL;
8864 if ( vtl->current_tpl ) {
8865 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8867 if ( vtl->current_tp_track )
8868 vik_track_calculate_bounds ( vtl->current_tp_track );
8871 if ( vtl->current_tp_track )
8872 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8873 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8877 vik_layer_emit_update ( VIK_LAYER(vtl) );
8884 Returns true if a waypoint or track is found near the requested event position for this particular layer
8885 The item found is automatically selected
8886 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8888 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8890 if ( event->button != 1 )
8893 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8896 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8900 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8902 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8904 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8905 WPSearchParams wp_params;
8906 wp_params.vvp = vvp;
8907 wp_params.x = event->x;
8908 wp_params.y = event->y;
8909 wp_params.draw_images = vtl->drawimages;
8910 wp_params.closest_wp_id = NULL;
8911 wp_params.closest_wp = NULL;
8913 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8915 if ( wp_params.closest_wp ) {
8918 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8920 // Too easy to move it so must be holding shift to start immediately moving it
8921 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8922 if ( event->state & GDK_SHIFT_MASK ||
8923 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8924 // Put into 'move buffer'
8925 // NB vvp & vw already set in tet
8926 tet->vtl = (gpointer)vtl;
8927 tet->is_waypoint = TRUE;
8929 marker_begin_move (tet, event->x, event->y);
8932 vtl->current_wp = wp_params.closest_wp;
8933 vtl->current_wp_id = wp_params.closest_wp_id;
8935 if ( event->type == GDK_2BUTTON_PRESS ) {
8936 if ( vtl->current_wp->image ) {
8937 menu_array_sublayer values;
8938 values[MA_VTL] = vtl;
8939 values[MA_MISC] = vtl->current_wp->image;
8940 trw_layer_show_picture ( values );
8944 vik_layer_emit_update ( VIK_LAYER(vtl) );
8950 // Used for both track and route lists
8951 TPSearchParams tp_params;
8952 tp_params.vvp = vvp;
8953 tp_params.x = event->x;
8954 tp_params.y = event->y;
8955 tp_params.closest_track_id = NULL;
8956 tp_params.closest_tp = NULL;
8957 tp_params.closest_tpl = NULL;
8958 tp_params.bbox = bbox;
8960 if (vtl->tracks_visible) {
8961 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8963 if ( tp_params.closest_tp ) {
8965 // Always select + highlight the track
8966 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8968 tet->is_waypoint = FALSE;
8970 // Select the Trackpoint
8971 // Can move it immediately when control held or it's the previously selected tp
8972 if ( event->state & GDK_CONTROL_MASK ||
8973 vtl->current_tpl == tp_params.closest_tpl ) {
8974 // Put into 'move buffer'
8975 // NB vvp & vw already set in tet
8976 tet->vtl = (gpointer)vtl;
8977 marker_begin_move (tet, event->x, event->y);
8980 vtl->current_tpl = tp_params.closest_tpl;
8981 vtl->current_tp_id = tp_params.closest_track_id;
8982 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8984 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8987 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
8989 vik_layer_emit_update ( VIK_LAYER(vtl) );
8994 // Try again for routes
8995 if (vtl->routes_visible) {
8996 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8998 if ( tp_params.closest_tp ) {
9000 // Always select + highlight the track
9001 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9003 tet->is_waypoint = FALSE;
9005 // Select the Trackpoint
9006 // Can move it immediately when control held or it's the previously selected tp
9007 if ( event->state & GDK_CONTROL_MASK ||
9008 vtl->current_tpl == tp_params.closest_tpl ) {
9009 // Put into 'move buffer'
9010 // NB vvp & vw already set in tet
9011 tet->vtl = (gpointer)vtl;
9012 marker_begin_move (tet, event->x, event->y);
9015 vtl->current_tpl = tp_params.closest_tpl;
9016 vtl->current_tp_id = tp_params.closest_track_id;
9017 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9019 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9022 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
9024 vik_layer_emit_update ( VIK_LAYER(vtl) );
9029 /* these aren't the droids you're looking for */
9030 vtl->current_wp = NULL;
9031 vtl->current_wp_id = NULL;
9032 trw_layer_cancel_current_tp ( vtl, FALSE );
9035 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9040 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9042 if ( event->button != 3 )
9045 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9048 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9051 /* Post menu for the currently selected item */
9053 /* See if a track is selected */
9054 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9055 if ( track && track->visible ) {
9057 if ( track->name ) {
9059 if ( vtl->track_right_click_menu )
9060 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9062 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9069 if ( track->is_route )
9070 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9072 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9074 if ( trkf && udataU.uuid ) {
9077 if ( track->is_route )
9078 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9080 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9082 trw_layer_sublayer_add_menu_items ( vtl,
9083 vtl->track_right_click_menu,
9085 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9091 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9097 /* See if a waypoint is selected */
9098 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9099 if ( waypoint && waypoint->visible ) {
9100 if ( waypoint->name ) {
9102 if ( vtl->wp_right_click_menu )
9103 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9105 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9108 udata.wp = waypoint;
9111 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9113 if ( wpf && udata.uuid ) {
9114 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9116 trw_layer_sublayer_add_menu_items ( vtl,
9117 vtl->wp_right_click_menu,
9119 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9124 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9133 /* background drawing hook, to be passed the viewport */
9134 static gboolean tool_sync_done = TRUE;
9136 static gboolean tool_sync(gpointer data)
9138 VikViewport *vvp = data;
9139 gdk_threads_enter();
9140 vik_viewport_sync(vvp);
9141 tool_sync_done = TRUE;
9142 gdk_threads_leave();
9146 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9149 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9150 gdk_gc_set_function ( t->gc, GDK_INVERT );
9151 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9152 vik_viewport_sync(t->vvp);
9157 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9159 VikViewport *vvp = t->vvp;
9160 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9161 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9165 if (tool_sync_done) {
9166 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9167 tool_sync_done = FALSE;
9171 static void marker_end_move ( tool_ed_t *t )
9173 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9174 g_object_unref ( t->gc );
9178 /*** Edit waypoint ****/
9180 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9182 tool_ed_t *t = g_new(tool_ed_t, 1);
9188 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9193 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9195 WPSearchParams params;
9196 tool_ed_t *t = data;
9197 VikViewport *vvp = t->vvp;
9199 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9206 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9209 if ( vtl->current_wp && vtl->current_wp->visible )
9211 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9213 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9215 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9216 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9218 if ( event->button == 3 )
9219 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9221 marker_begin_move(t, event->x, event->y);
9228 params.x = event->x;
9229 params.y = event->y;
9230 params.draw_images = vtl->drawimages;
9231 params.closest_wp_id = NULL;
9232 params.closest_wp = NULL;
9233 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9234 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9236 if ( event->button == 3 )
9237 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9239 marker_begin_move(t, event->x, event->y);
9242 else if ( params.closest_wp )
9244 if ( event->button == 3 )
9245 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9247 vtl->waypoint_rightclick = FALSE;
9249 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9251 vtl->current_wp = params.closest_wp;
9252 vtl->current_wp_id = params.closest_wp_id;
9254 /* could make it so don't update if old WP is off screen and new is null but oh well */
9255 vik_layer_emit_update ( VIK_LAYER(vtl) );
9259 vtl->current_wp = NULL;
9260 vtl->current_wp_id = NULL;
9261 vtl->waypoint_rightclick = FALSE;
9262 vik_layer_emit_update ( VIK_LAYER(vtl) );
9266 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9268 tool_ed_t *t = data;
9269 VikViewport *vvp = t->vvp;
9271 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9276 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9279 if ( event->state & GDK_CONTROL_MASK )
9281 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9283 new_coord = tp->coord;
9287 if ( event->state & GDK_SHIFT_MASK )
9289 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9290 if ( wp && wp != vtl->current_wp )
9291 new_coord = wp->coord;
9296 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9298 marker_moveto ( t, x, y );
9305 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9307 tool_ed_t *t = data;
9308 VikViewport *vvp = t->vvp;
9310 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9313 if ( t->holding && event->button == 1 )
9316 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9319 if ( event->state & GDK_CONTROL_MASK )
9321 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9323 new_coord = tp->coord;
9327 if ( event->state & GDK_SHIFT_MASK )
9329 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9330 if ( wp && wp != vtl->current_wp )
9331 new_coord = wp->coord;
9334 marker_end_move ( t );
9336 vtl->current_wp->coord = new_coord;
9338 trw_layer_calculate_bounds_waypoints ( vtl );
9339 vik_layer_emit_update ( VIK_LAYER(vtl) );
9342 /* PUT IN RIGHT PLACE!!! */
9343 if ( event->button == 3 && vtl->waypoint_rightclick )
9345 if ( vtl->wp_right_click_menu )
9346 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9347 if ( vtl->current_wp ) {
9348 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9349 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 );
9350 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9352 vtl->waypoint_rightclick = FALSE;
9357 /*** New track ****/
9359 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9366 GdkDrawable *drawable;
9372 * Draw specified pixmap
9374 static gboolean draw_sync ( gpointer data )
9376 draw_sync_t *ds = (draw_sync_t*) data;
9377 // Sometimes don't want to draw
9378 // normally because another update has taken precedent such as panning the display
9379 // which means this pixmap is no longer valid
9380 if ( ds->vtl->draw_sync_do ) {
9381 gdk_threads_enter();
9382 gdk_draw_drawable (ds->drawable,
9385 0, 0, 0, 0, -1, -1);
9386 ds->vtl->draw_sync_done = TRUE;
9387 gdk_threads_leave();
9393 static gchar* distance_string (gdouble distance)
9397 /* draw label with distance */
9398 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9399 switch (dist_units) {
9400 case VIK_UNITS_DISTANCE_MILES:
9401 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9402 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9403 } else if (distance < 1609.4) {
9404 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9406 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9410 // VIK_UNITS_DISTANCE_KILOMETRES
9411 if (distance >= 1000 && distance < 100000) {
9412 g_sprintf(str, "%3.2f km", distance/1000.0);
9413 } else if (distance < 1000) {
9414 g_sprintf(str, "%d m", (int)distance);
9416 g_sprintf(str, "%d km", (int)distance/1000);
9420 return g_strdup (str);
9424 * Actually set the message in statusbar
9426 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9428 // Only show elevation data when track has some elevation properties
9429 gchar str_gain_loss[64];
9430 str_gain_loss[0] = '\0';
9431 gchar str_last_step[64];
9432 str_last_step[0] = '\0';
9433 gchar *str_total = distance_string (distance);
9435 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9436 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9437 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9439 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9442 if ( last_step > 0 ) {
9443 gchar *tmp = distance_string (last_step);
9444 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9448 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9450 // Write with full gain/loss information
9451 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9452 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9454 g_free ( str_total );
9458 * Figure out what information should be set in the statusbar and then write it
9460 static void update_statusbar ( VikTrwLayer *vtl )
9462 // Get elevation data
9463 gdouble elev_gain, elev_loss;
9464 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9466 /* Find out actual distance of current track */
9467 gdouble distance = vik_track_get_length (vtl->current_track);
9469 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9473 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9475 /* if we haven't sync'ed yet, we don't have time to do more. */
9476 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9477 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9479 static GdkPixmap *pixmap = NULL;
9481 // Need to check in case window has been resized
9482 w1 = vik_viewport_get_width(vvp);
9483 h1 = vik_viewport_get_height(vvp);
9485 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9487 gdk_drawable_get_size (pixmap, &w2, &h2);
9488 if (w1 != w2 || h1 != h2) {
9489 g_object_unref ( G_OBJECT ( pixmap ) );
9490 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9493 // Reset to background
9494 gdk_draw_drawable (pixmap,
9495 vtl->current_track_newpoint_gc,
9496 vik_viewport_get_pixmap(vvp),
9497 0, 0, 0, 0, -1, -1);
9499 draw_sync_t *passalong;
9502 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9504 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9505 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9506 // thus when we come to reset to the background it would include what we have already drawn!!
9507 gdk_draw_line ( pixmap,
9508 vtl->current_track_newpoint_gc,
9509 x1, y1, event->x, event->y );
9510 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9512 /* Find out actual distance of current track */
9513 gdouble distance = vik_track_get_length (vtl->current_track);
9515 // Now add distance to where the pointer is //
9518 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9519 vik_coord_to_latlon ( &coord, &ll );
9520 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9521 distance = distance + last_step;
9523 // Get elevation data
9524 gdouble elev_gain, elev_loss;
9525 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9527 // Adjust elevation data (if available) for the current pointer position
9529 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9530 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9531 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9532 // Adjust elevation of last track point
9533 if ( elev_new > last_tpt->altitude )
9535 elev_gain += elev_new - last_tpt->altitude;
9538 elev_loss += last_tpt->altitude - elev_new;
9543 // Display of the distance 'tooltip' during track creation is controlled by a preference
9545 if ( a_vik_get_create_track_tooltip() ) {
9547 gchar *str = distance_string (distance);
9549 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9550 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9551 pango_layout_set_text (pl, str, -1);
9553 pango_layout_get_pixel_size ( pl, &wd, &hd );
9556 // offset from cursor a bit depending on font size
9560 // Create a background block to make the text easier to read over the background map
9561 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9562 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9563 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9565 g_object_unref ( G_OBJECT ( pl ) );
9566 g_object_unref ( G_OBJECT ( background_block_gc ) );
9570 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9571 passalong->vtl = vtl;
9572 passalong->pixmap = pixmap;
9573 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9574 passalong->gc = vtl->current_track_newpoint_gc;
9578 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9580 // Update statusbar with full gain/loss information
9581 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9583 // draw pixmap when we have time to
9584 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9585 vtl->draw_sync_done = FALSE;
9586 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9588 return VIK_LAYER_TOOL_ACK;
9591 // NB vtl->current_track must be valid
9592 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9595 if ( vtl->current_track->trackpoints ) {
9596 // TODO rework this...
9597 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9598 GList *last = g_list_last(vtl->current_track->trackpoints);
9599 g_free ( last->data );
9600 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9602 vik_track_calculate_bounds ( vtl->current_track );
9606 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9608 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9609 // Bin track if only one point as it's not very useful
9610 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9611 if ( vtl->current_track->is_route )
9612 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9614 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9616 vtl->current_track = NULL;
9617 vik_layer_emit_update ( VIK_LAYER(vtl) );
9619 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9620 undo_trackpoint_add ( vtl );
9621 update_statusbar ( vtl );
9622 vik_layer_emit_update ( VIK_LAYER(vtl) );
9629 * Common function to handle trackpoint button requests on either a route or a track
9630 * . enables adding a point via normal click
9631 * . enables removal of last point via right click
9632 * . finishing of the track or route via double clicking
9634 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9638 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9641 if ( event->button == 2 ) {
9642 // As the display is panning, the new track pixmap is now invalid so don't draw it
9643 // otherwise this drawing done results in flickering back to an old image
9644 vtl->draw_sync_do = FALSE;
9648 if ( event->button == 3 )
9650 if ( !vtl->current_track )
9652 undo_trackpoint_add ( vtl );
9653 update_statusbar ( vtl );
9654 vik_layer_emit_update ( VIK_LAYER(vtl) );
9658 if ( event->type == GDK_2BUTTON_PRESS )
9660 /* subtract last (duplicate from double click) tp then end */
9661 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9663 /* undo last, then end */
9664 undo_trackpoint_add ( vtl );
9665 vtl->current_track = NULL;
9667 vik_layer_emit_update ( VIK_LAYER(vtl) );
9671 tp = vik_trackpoint_new();
9672 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9674 /* snap to other TP */
9675 if ( event->state & GDK_CONTROL_MASK )
9677 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9679 tp->coord = other_tp->coord;
9682 tp->newsegment = FALSE;
9683 tp->has_timestamp = FALSE;
9686 if ( vtl->current_track ) {
9687 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9688 /* Auto attempt to get elevation from DEM data (if it's available) */
9689 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9692 vtl->ct_x1 = vtl->ct_x2;
9693 vtl->ct_y1 = vtl->ct_y2;
9694 vtl->ct_x2 = event->x;
9695 vtl->ct_y2 = event->y;
9697 vik_layer_emit_update ( VIK_LAYER(vtl) );
9701 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9703 // if we were running the route finder, cancel it
9704 vtl->route_finder_started = FALSE;
9706 // ----------------------------------------------------- if current is a route - switch to new track
9707 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9709 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9710 if ( a_vik_get_ask_for_create_track_name() ) {
9711 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
9715 new_track_create_common ( vtl, name );
9718 return tool_new_track_or_route_click ( vtl, event, vvp );
9721 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9723 if ( event->button == 2 ) {
9724 // Pan moving ended - enable potential point drawing again
9725 vtl->draw_sync_do = TRUE;
9726 vtl->draw_sync_done = TRUE;
9730 /*** New route ****/
9732 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9737 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9739 // if we were running the route finder, cancel it
9740 vtl->route_finder_started = FALSE;
9742 // -------------------------- if current is a track - switch to new route,
9743 if ( event->button == 1 && ( ! vtl->current_track ||
9744 (vtl->current_track && !vtl->current_track->is_route ) ) )
9746 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9747 if ( a_vik_get_ask_for_create_track_name() ) {
9748 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
9752 new_route_create_common ( vtl, name );
9755 return tool_new_track_or_route_click ( vtl, event, vvp );
9758 /*** New waypoint ****/
9760 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9765 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9768 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9770 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9771 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9772 trw_layer_calculate_bounds_waypoints ( vtl );
9773 vik_layer_emit_update ( VIK_LAYER(vtl) );
9779 /*** Edit trackpoint ****/
9781 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9783 tool_ed_t *t = g_new(tool_ed_t, 1);
9789 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9794 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9796 tool_ed_t *t = data;
9797 VikViewport *vvp = t->vvp;
9798 TPSearchParams params;
9799 /* OUTDATED DOCUMENTATION:
9800 find 5 pixel range on each side. then put these UTM, and a pointer
9801 to the winning track name (and maybe the winning track itself), and a
9802 pointer to the winning trackpoint, inside an array or struct. pass
9803 this along, do a foreach on the tracks which will do a foreach on the
9806 params.x = event->x;
9807 params.y = event->y;
9808 params.closest_track_id = NULL;
9809 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9810 params.closest_tp = NULL;
9811 params.closest_tpl = NULL;
9812 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9814 if ( event->button != 1 )
9817 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9820 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9823 if ( vtl->current_tpl )
9825 /* 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.) */
9826 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9827 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9832 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9834 if ( current_tr->visible &&
9835 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9836 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9837 marker_begin_move ( t, event->x, event->y );
9843 if ( vtl->tracks_visible )
9844 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9846 if ( params.closest_tp )
9848 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9849 vtl->current_tpl = params.closest_tpl;
9850 vtl->current_tp_id = params.closest_track_id;
9851 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9852 trw_layer_tpwin_init ( vtl );
9853 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9854 vik_layer_emit_update ( VIK_LAYER(vtl) );
9858 if ( vtl->routes_visible )
9859 g_hash_table_foreach ( vtl->routes, (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->routes_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->routes, 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 /* these aren't the droids you're looking for */
9877 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9879 tool_ed_t *t = data;
9880 VikViewport *vvp = t->vvp;
9882 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9888 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9891 if ( event->state & GDK_CONTROL_MASK )
9893 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9894 if ( tp && tp != vtl->current_tpl->data )
9895 new_coord = tp->coord;
9897 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9900 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9901 marker_moveto ( t, x, y );
9909 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9911 tool_ed_t *t = data;
9912 VikViewport *vvp = t->vvp;
9914 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9916 if ( event->button != 1)
9921 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9924 if ( event->state & GDK_CONTROL_MASK )
9926 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9927 if ( tp && tp != vtl->current_tpl->data )
9928 new_coord = tp->coord;
9931 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9932 if ( vtl->current_tp_track )
9933 vik_track_calculate_bounds ( vtl->current_tp_track );
9935 marker_end_move ( t );
9937 /* diff dist is diff from orig */
9939 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name, vtl->current_tp_track->is_route );
9941 vik_layer_emit_update ( VIK_LAYER(vtl) );
9948 /*** Extended Route Finder ***/
9950 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9955 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9958 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9961 vik_layer_emit_update ( VIK_LAYER(vtl) );
9963 /* remove last ' to:...' */
9964 if ( vtl->current_track->comment ) {
9965 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9966 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9967 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9968 last_to - vtl->current_track->comment - 1);
9969 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9976 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9979 if ( !vtl ) return FALSE;
9980 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9981 if ( event->button == 3 && vtl->current_track ) {
9982 tool_extended_route_finder_undo ( vtl );
9984 else if ( event->button == 2 ) {
9985 vtl->draw_sync_do = FALSE;
9988 // if we started the track but via undo deleted all the track points, begin again
9989 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
9990 return tool_new_track_or_route_click ( vtl, event, vvp );
9992 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
9993 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
9994 struct LatLon start, end;
9996 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
9997 vik_coord_to_latlon ( &(tp_start->coord), &start );
9998 vik_coord_to_latlon ( &(tmp), &end );
10000 vtl->route_finder_started = TRUE;
10001 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10003 // update UI to let user know what's going on
10004 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10005 VikRoutingEngine *engine = vik_routing_default_engine ( );
10007 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10010 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10011 vik_routing_engine_get_label ( engine ),
10012 start.lat, start.lon, end.lat, end.lon );
10013 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10015 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10018 /* Give GTK a change to display the new status bar before querying the web */
10019 while ( gtk_events_pending ( ) )
10020 gtk_main_iteration ( );
10022 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10024 /* Update UI to say we're done */
10025 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10026 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10027 vik_routing_engine_get_label ( engine ),
10028 start.lat, start.lon, end.lat, end.lon )
10029 : g_strdup_printf ( _("Error getting route from %s."),
10030 vik_routing_engine_get_label ( engine ) );
10031 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10034 vik_layer_emit_update ( VIK_LAYER(vtl) );
10036 vtl->current_track = NULL;
10038 // create a new route where we will add the planned route to
10039 gboolean ret = tool_new_route_click( vtl, event, vvp );
10041 vtl->route_finder_started = TRUE;
10048 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10050 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10051 vtl->route_finder_started = FALSE;
10052 vtl->current_track = NULL;
10053 vik_layer_emit_update ( VIK_LAYER(vtl) );
10055 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10056 tool_extended_route_finder_undo ( vtl );
10063 /*** Show picture ****/
10065 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10070 /* Params are: vvp, event, last match found or NULL */
10071 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10073 if ( wp->image && wp->visible )
10075 gint x, y, slackx, slacky;
10076 GdkEventButton *event = (GdkEventButton *) params[1];
10078 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10079 slackx = wp->image_width / 2;
10080 slacky = wp->image_height / 2;
10081 if ( x <= event->x + slackx && x >= event->x - slackx
10082 && y <= event->y + slacky && y >= event->y - slacky )
10084 params[2] = wp->image; /* we've found a match. however continue searching
10085 * since we want to find the last match -- that
10086 * is, the match that was drawn last. */
10091 static void trw_layer_show_picture ( menu_array_sublayer values )
10093 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10095 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10096 #else /* WINDOWS */
10097 GError *err = NULL;
10098 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10099 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10100 g_free ( quoted_file );
10101 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10103 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() );
10104 g_error_free ( err );
10107 #endif /* WINDOWS */
10110 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10112 gpointer params[3] = { vvp, event, NULL };
10113 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10115 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10118 static menu_array_sublayer values;
10119 values[MA_VTL] = vtl;
10120 values[MA_MISC] = params[2];
10121 trw_layer_show_picture ( values );
10122 return TRUE; /* found a match */
10125 return FALSE; /* go through other layers, searching for a match */
10128 /***************************************************************************
10130 ***************************************************************************/
10133 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10135 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10136 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10139 /* Structure for thumbnail creating data used in the background thread */
10141 VikTrwLayer *vtl; // Layer needed for redrawing
10142 GSList *pics; // Image list
10143 } thumbnail_create_thread_data;
10145 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10147 guint total = g_slist_length(tctd->pics), done = 0;
10148 while ( tctd->pics )
10150 a_thumbnails_create ( (gchar *) tctd->pics->data );
10151 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10153 return -1; /* Abort thread */
10155 tctd->pics = tctd->pics->next;
10158 // Redraw to show the thumbnails as they are now created
10159 if ( IS_VIK_LAYER(tctd->vtl) )
10160 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10165 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10167 while ( tctd->pics )
10169 g_free ( tctd->pics->data );
10170 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10175 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10177 if ( ! vtl->has_verified_thumbnails )
10179 GSList *pics = NULL;
10180 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10183 gint len = g_slist_length ( pics );
10184 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10185 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10188 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10190 (vik_thr_func) create_thumbnails_thread,
10192 (vik_thr_free_func) thumbnail_create_thread_free,
10200 static const gchar* my_track_colors ( gint ii )
10202 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10214 // Fast and reliable way of returning a colour
10215 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10218 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10220 GHashTableIter iter;
10221 gpointer key, value;
10225 g_hash_table_iter_init ( &iter, vtl->tracks );
10227 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10229 // Tracks get a random spread of colours if not already assigned
10230 if ( ! VIK_TRACK(value)->has_color ) {
10231 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10232 VIK_TRACK(value)->color = vtl->track_color;
10234 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10236 VIK_TRACK(value)->has_color = TRUE;
10239 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10242 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10248 g_hash_table_iter_init ( &iter, vtl->routes );
10250 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10252 // Routes get an intermix of reds
10253 if ( ! VIK_TRACK(value)->has_color ) {
10255 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10257 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10258 VIK_TRACK(value)->has_color = TRUE;
10261 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10268 * (Re)Calculate the bounds of the waypoints in this layer,
10269 * This should be called whenever waypoints are changed
10271 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10273 struct LatLon topleft = { 0.0, 0.0 };
10274 struct LatLon bottomright = { 0.0, 0.0 };
10277 GHashTableIter iter;
10278 gpointer key, value;
10280 g_hash_table_iter_init ( &iter, vtl->waypoints );
10282 // Set bounds to first point
10283 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10284 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10285 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10288 // Ensure there is another point...
10289 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10291 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10293 // See if this point increases the bounds.
10294 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10296 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10297 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10298 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10299 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10303 vtl->waypoints_bbox.north = topleft.lat;
10304 vtl->waypoints_bbox.east = bottomright.lon;
10305 vtl->waypoints_bbox.south = bottomright.lat;
10306 vtl->waypoints_bbox.west = topleft.lon;
10309 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10311 vik_track_calculate_bounds ( trk );
10314 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10316 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10317 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10320 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10322 if ( ! VIK_LAYER(vtl)->vt )
10325 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10326 if ( g_hash_table_size (vtl->tracks) > 1 )
10327 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10329 if ( g_hash_table_size (vtl->routes) > 1 )
10330 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10332 if ( g_hash_table_size (vtl->waypoints) > 1 )
10333 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10336 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10338 if ( VIK_LAYER(vtl)->realized )
10339 trw_layer_verify_thumbnails ( vtl, vvp );
10340 trw_layer_track_alloc_colors ( vtl );
10342 trw_layer_calculate_bounds_waypoints ( vtl );
10343 trw_layer_calculate_bounds_tracks ( vtl );
10345 // Apply treeview sort after loading all the tracks for this layer
10346 // (rather than sorted insert on each individual track additional)
10347 // and after subsequent changes to the properties as the specified order may have changed.
10348 // since the sorting of a treeview section is now very quick
10349 // NB sorting is also performed after every name change as well to maintain the list order
10350 trw_layer_sort_all ( vtl );
10352 // Setting metadata time if not otherwise set
10353 if ( vtl->metadata ) {
10355 gboolean need_to_set_time = TRUE;
10356 if ( vtl->metadata->timestamp ) {
10357 need_to_set_time = FALSE;
10358 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10359 need_to_set_time = TRUE;
10362 if ( need_to_set_time ) {
10363 // Could rewrite this as a general get first time of a TRW Layer function
10364 GTimeVal timestamp;
10365 timestamp.tv_usec = 0;
10366 gboolean has_timestamp = FALSE;
10369 gl = g_hash_table_get_values ( vtl->tracks );
10370 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10371 gl = g_list_first ( gl );
10373 // Check times of tracks
10375 // Only need to check the first track as they have been sorted by time
10376 VikTrack *trk = (VikTrack*)gl->data;
10377 // Assume trackpoints already sorted by time
10378 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10379 if ( tpt && tpt->has_timestamp ) {
10380 timestamp.tv_sec = tpt->timestamp;
10381 has_timestamp = TRUE;
10383 g_list_free ( gl );
10386 if ( !has_timestamp ) {
10387 // 'Last' resort - current time
10388 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10389 g_get_current_time ( ×tamp );
10391 // Check times of waypoints
10392 gl = g_hash_table_get_values ( vtl->waypoints );
10394 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10395 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10396 if ( wpt->has_timestamp ) {
10397 if ( timestamp.tv_sec > wpt->timestamp ) {
10398 timestamp.tv_sec = wpt->timestamp;
10399 has_timestamp = TRUE;
10403 g_list_free ( gl );
10406 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10411 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10413 return vtl->coord_mode;
10417 * Uniquify the whole layer
10418 * Also requires the layers panel as the names shown there need updating too
10419 * Returns whether the operation was successful or not
10421 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10423 if ( vtl && vlp ) {
10424 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10425 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10426 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10432 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10434 vik_coord_convert ( &(wp->coord), *dest_mode );
10437 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10439 vik_track_convert ( tr, *dest_mode );
10442 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10444 if ( vtl->coord_mode != dest_mode )
10446 vtl->coord_mode = dest_mode;
10447 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10448 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10449 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10453 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10455 vtl->menu_selection = selection;
10458 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10460 return (vtl->menu_selection);
10463 /* ----------- Downloading maps along tracks --------------- */
10465 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10467 /* TODO: calculating based on current size of viewport */
10468 const gdouble w_at_zoom_0_125 = 0.0013;
10469 const gdouble h_at_zoom_0_125 = 0.0011;
10470 gdouble zoom_factor = zoom_level/0.125;
10472 wh->lat = h_at_zoom_0_125 * zoom_factor;
10473 wh->lon = w_at_zoom_0_125 * zoom_factor;
10475 return 0; /* all OK */
10478 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10480 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10481 (dist->lat >= ABS(to->north_south - from->north_south)))
10484 VikCoord *coord = g_malloc(sizeof(VikCoord));
10485 coord->mode = VIK_COORD_LATLON;
10487 if (ABS(gradient) < 1) {
10488 if (from->east_west > to->east_west)
10489 coord->east_west = from->east_west - dist->lon;
10491 coord->east_west = from->east_west + dist->lon;
10492 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10494 if (from->north_south > to->north_south)
10495 coord->north_south = from->north_south - dist->lat;
10497 coord->north_south = from->north_south + dist->lat;
10498 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10504 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10506 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10507 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10509 VikCoord *next = from;
10511 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10513 list = g_list_prepend(list, next);
10519 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10521 typedef struct _Rect {
10526 #define GLRECT(iter) ((Rect *)((iter)->data))
10529 GList *rects_to_download = NULL;
10532 if (get_download_area_width(vvp, zoom_level, &wh))
10535 GList *iter = tr->trackpoints;
10539 gboolean new_map = TRUE;
10540 VikCoord *cur_coord, tl, br;
10543 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10545 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10546 rect = g_malloc(sizeof(Rect));
10549 rect->center = *cur_coord;
10550 rects_to_download = g_list_prepend(rects_to_download, rect);
10555 gboolean found = FALSE;
10556 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10557 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10568 GList *fillins = NULL;
10569 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10570 /* seems that ATM the function get_next_coord works only for LATLON */
10571 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10572 /* fill-ins for far apart points */
10573 GList *cur_rect, *next_rect;
10574 for (cur_rect = rects_to_download;
10575 (next_rect = cur_rect->next) != NULL;
10576 cur_rect = cur_rect->next) {
10577 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10578 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10579 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10583 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10586 GList *fiter = fillins;
10588 cur_coord = (VikCoord *)(fiter->data);
10589 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10590 rect = g_malloc(sizeof(Rect));
10593 rect->center = *cur_coord;
10594 rects_to_download = g_list_prepend(rects_to_download, rect);
10595 fiter = fiter->next;
10599 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10600 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10604 for (iter = fillins; iter; iter = iter->next)
10605 g_free(iter->data);
10606 g_list_free(fillins);
10608 if (rects_to_download) {
10609 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10610 g_free(rect_iter->data);
10611 g_list_free(rects_to_download);
10615 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10619 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10620 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10621 gint selected_zoom, default_zoom;
10623 VikTrwLayer *vtl = values[MA_VTL];
10624 VikLayersPanel *vlp = values[MA_VLP];
10626 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10627 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10629 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10633 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10635 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10636 int num_maps = g_list_length(vmls);
10639 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10643 // Convert from list of vmls to list of names. Allowing the user to select one of them
10644 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10645 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10647 gchar **np = map_names;
10648 VikMapsLayer **lp = map_layers;
10650 for (i = 0; i < num_maps; i++) {
10651 vml = (VikMapsLayer *)(vmls->data);
10653 *np++ = vik_maps_layer_get_map_label(vml);
10656 // Mark end of the array lists
10660 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10661 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10662 if (cur_zoom == zoom_vals[default_zoom])
10665 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10667 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10670 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10673 for (i = 0; i < num_maps; i++)
10674 g_free(map_names[i]);
10676 g_free(map_layers);
10682 /**** lowest waypoint number calculation ***/
10683 static gint highest_wp_number_name_to_number(const gchar *name) {
10684 if ( strlen(name) == 3 ) {
10685 int n = atoi(name);
10686 if ( n < 100 && name[0] != '0' )
10688 if ( n < 10 && name[0] != '0' )
10696 static void highest_wp_number_reset(VikTrwLayer *vtl)
10698 vtl->highest_wp_number = -1;
10701 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10703 /* if is bigger that top, add it */
10704 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10705 if ( new_wp_num > vtl->highest_wp_number )
10706 vtl->highest_wp_number = new_wp_num;
10709 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10711 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10712 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10713 if ( vtl->highest_wp_number == old_wp_num ) {
10715 vtl->highest_wp_number--;
10717 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10718 /* search down until we find something that *does* exist */
10720 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10721 vtl->highest_wp_number--;
10722 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10727 /* get lowest unused number */
10728 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10731 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10733 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10734 return g_strdup(buf);
10738 * trw_layer_create_track_list_both:
10740 * Create the latest list of tracks and routes
10742 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10744 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10745 GList *tracks = NULL;
10746 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10747 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10749 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10752 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10754 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10756 gchar *title = NULL;
10757 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10758 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10760 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10762 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10766 static void trw_layer_track_list_dialog ( menu_array_layer values )
10768 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10770 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10771 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10775 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10777 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10779 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10780 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );