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"
53 #include "geonamessearch.h"
54 #ifdef VIK_CONFIG_OPENSTREETMAP
55 #include "osm-traces.h"
58 #include "datasources.h"
59 #include "datasource_gps.h"
60 #include "vikexttool_datasources.h"
64 #include "vikrouting.h"
66 #include "icons/icons.h"
80 #include <gdk/gdkkeysyms.h>
82 #include <glib/gstdio.h>
83 #include <glib/gi18n.h>
85 #define VIK_TRW_LAYER_TRACK_GC 6
86 #define VIK_TRW_LAYER_TRACK_GCS 10
87 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
88 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
89 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
90 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
91 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
92 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
94 #define DRAWMODE_BY_TRACK 0
95 #define DRAWMODE_BY_SPEED 1
96 #define DRAWMODE_ALL_SAME_COLOR 2
97 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
98 // as we are (re)calculating the colour for every point
103 /* this is how it knows when you click if you are clicking close to a trackpoint. */
104 #define TRACKPOINT_SIZE_APPROX 5
105 #define WAYPOINT_SIZE_APPROX 5
107 #define MIN_STOP_LENGTH 15
108 #define MAX_STOP_LENGTH 86400
109 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
110 /* this is multiplied by user-inputted value from 1-100. */
112 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
114 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
116 FS_XX_SMALL = 0, // 'xx-small'
119 FS_MEDIUM, // DEFAULT
126 struct _VikTrwLayer {
129 GHashTable *tracks_iters;
131 GHashTable *routes_iters;
132 GHashTable *waypoints_iters;
133 GHashTable *waypoints;
134 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
135 gboolean tracks_visible, routes_visible, waypoints_visible;
136 LatLonBBox waypoints_bbox;
138 gboolean track_draw_labels;
141 guint8 drawpoints_size;
142 guint8 drawelevation;
143 guint8 elevation_factor;
147 guint8 drawdirections;
148 guint8 drawdirections_size;
149 guint8 line_thickness;
150 guint8 bg_line_thickness;
151 vik_layer_sort_order_t track_sort_order;
154 VikTRWMetadata *metadata;
156 PangoLayout *tracklabellayout;
157 font_size_t track_font_size;
158 gchar *track_fsize_str;
162 gboolean wp_draw_symbols;
163 font_size_t wp_font_size;
165 vik_layer_sort_order_t wp_sort_order;
167 gdouble track_draw_speed_factor;
169 GdkGC *track_1color_gc;
170 GdkColor track_color;
171 GdkGC *current_track_gc;
172 // Separate GC for a track's potential new point as drawn via separate method
173 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
174 GdkGC *current_track_newpoint_gc;
175 GdkGC *track_bg_gc; GdkColor track_bg_color;
176 GdkGC *waypoint_gc; GdkColor waypoint_color;
177 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
178 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
180 GdkFont *waypoint_font;
181 VikTrack *current_track; // ATM shared between new tracks and new routes
182 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
183 gboolean draw_sync_done;
184 gboolean draw_sync_do;
186 VikCoordMode coord_mode;
188 /* wp editing tool */
189 VikWaypoint *current_wp;
190 gpointer current_wp_id;
192 gboolean waypoint_rightclick;
194 /* track editing tool */
196 VikTrack *current_tp_track;
197 gpointer current_tp_id;
198 VikTrwLayerTpwin *tpwin;
200 /* track editing tool -- more specifically, moving tps */
203 /* route finder tool */
204 gboolean route_finder_started;
205 VikCoord route_finder_coord;
206 gboolean route_finder_check_added_track;
207 VikTrack *route_finder_added_track;
208 VikTrack *route_finder_current_track;
209 gboolean route_finder_append;
216 guint16 image_cache_size;
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
221 gboolean has_verified_thumbnails;
223 GtkMenu *wp_right_click_menu;
224 GtkMenu *track_right_click_menu;
227 VikStdLayerMenuItem menu_selection;
229 gint highest_wp_number;
232 GtkWidget *tracks_analysis_dialog;
235 /* A caached waypoint image. */
238 gchar *image; /* filename */
241 struct DrawingParams {
246 guint16 width, height;
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
249 const VikCoord *center;
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
256 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
261 MA_SUBTYPE, // OR END for Layer only
270 typedef gpointer menu_array_layer[2];
271 typedef gpointer menu_array_sublayer[MA_LAST];
273 static void trw_layer_delete_item ( menu_array_sublayer values );
274 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
277 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
278 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
280 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
283 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
286 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
287 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292 static void trw_layer_goto_track_center ( menu_array_sublayer values );
293 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295 static void trw_layer_merge_with_other ( menu_array_sublayer values );
296 static void trw_layer_append_track ( menu_array_sublayer values );
297 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300 static void trw_layer_split_segments ( menu_array_sublayer values );
301 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304 static void trw_layer_reverse ( menu_array_sublayer values );
305 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307 static void trw_layer_show_picture ( menu_array_sublayer values );
308 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
310 static void trw_layer_centerize ( menu_array_layer values );
311 static void trw_layer_auto_view ( menu_array_layer values );
312 static void trw_layer_goto_wp ( menu_array_layer values );
313 static void trw_layer_new_wp ( menu_array_layer values );
314 static void trw_layer_new_track ( menu_array_layer values );
315 static void trw_layer_new_route ( menu_array_layer values );
316 static void trw_layer_finish_track ( menu_array_layer values );
317 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318 static void trw_layer_auto_tracks_view ( menu_array_layer values );
319 static void trw_layer_delete_all_tracks ( menu_array_layer values );
320 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
325 #ifdef VIK_CONFIG_GEOTAG
326 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328 static void trw_layer_geotagging_track ( menu_array_sublayer values );
329 static void trw_layer_geotagging ( menu_array_layer values );
331 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333 static void trw_layer_acquire_url_cb ( menu_array_layer values );
334 #ifdef VIK_CONFIG_OPENSTREETMAP
335 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
338 #ifdef VIK_CONFIG_GEOCACHES
339 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
341 #ifdef VIK_CONFIG_GEOTAG
342 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
344 static void trw_layer_acquire_file_cb ( menu_array_layer values );
345 static void trw_layer_gps_upload ( menu_array_layer values );
347 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
348 static void trw_layer_track_list_dialog ( menu_array_layer values );
349 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
351 // Specific route versions:
352 // Most track handling functions can handle operating on the route list
353 // However these ones are easier in separate functions
354 static void trw_layer_auto_routes_view ( menu_array_layer values );
355 static void trw_layer_delete_all_routes ( menu_array_layer values );
356 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
359 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
364 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
365 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
367 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
372 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
379 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
389 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
391 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *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 },
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 },
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 },
441 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
442 (VikToolConstructorFunc) tool_edit_waypoint_create,
443 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
445 (VikToolMouseFunc) tool_edit_waypoint_click,
446 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
447 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
449 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
451 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
452 (VikToolConstructorFunc) tool_edit_trackpoint_create,
453 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
455 (VikToolMouseFunc) tool_edit_trackpoint_click,
456 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
457 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
459 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
461 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
462 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
463 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
465 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
467 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
468 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
469 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
471 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
475 TOOL_CREATE_WAYPOINT=0,
479 TOOL_EDIT_TRACKPOINT,
485 /****** PARAMETERS ******/
487 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
488 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
490 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
491 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
493 #define MIN_POINT_SIZE 2
494 #define MAX_POINT_SIZE 10
496 #define MIN_ARROW_SIZE 3
497 #define MAX_ARROW_SIZE 20
499 static VikLayerParamScale params_scales[] = {
500 /* min max step digits */
501 { 1, 10, 1, 0 }, /* line_thickness */
502 { 0, 100, 1, 0 }, /* track draw speed factor */
503 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
504 /* 5 * step == how much to turn */
505 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
506 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
507 { 5, 500, 5, 0 }, // 5: image cache_size - " "
508 { 0, 8, 1, 0 }, // 6: Background line thickness
509 { 1, 64, 1, 0 }, /* wpsize */
510 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
511 { 1, 100, 1, 0 }, // 9: elevation factor
512 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
513 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
516 static gchar* params_font_sizes[] = {
517 N_("Extra Extra Small"),
523 N_("Extra Extra Large"),
526 // Needs to align with vik_layer_sort_order_t
527 static gchar* params_sort_order[] = {
529 N_("Name Ascending"),
530 N_("Name Descending"),
534 static VikLayerParamData black_color_default ( void ) {
535 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
537 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
538 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
539 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
540 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
541 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
542 static VikLayerParamData trackbgcolor_default ( void ) {
543 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
545 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
546 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
547 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
549 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
550 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
551 static VikLayerParamData wptextcolor_default ( void ) {
552 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
554 static VikLayerParamData wpbgcolor_default ( void ) {
555 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
557 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
558 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
560 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
561 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
562 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
564 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
566 static VikLayerParamData string_default ( void )
568 VikLayerParamData data;
573 VikLayerParam trw_layer_params[] = {
574 { 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 },
575 { 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 },
576 { 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 },
578 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
579 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
580 { 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 },
581 { 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 },
582 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
583 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
584 { 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 },
585 { 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 },
586 { 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 },
587 { 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 },
588 { 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 },
589 { 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 },
590 { 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 },
591 { 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 },
592 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
593 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 },
594 { 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 },
596 { 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 },
597 { 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 },
598 { 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,
599 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
600 { 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 },
602 { 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 },
603 { 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 },
604 { 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 },
605 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
606 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
607 { 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 },
608 { 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 },
609 { 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 },
610 { 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 },
611 { 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 },
613 { 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 },
614 { 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 },
615 { 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 },
616 { 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 },
618 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
619 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
620 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
621 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
626 // Sublayer visibilities
674 *** 1) Add to trw_layer_params and enumeration
675 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
678 /****** END PARAMETERS ******/
680 /* Layer Interface function definitions */
681 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
682 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
683 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
684 static void trw_layer_free ( VikTrwLayer *trwlayer );
685 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
686 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
687 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
688 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
689 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
690 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
691 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
692 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
693 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
694 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
695 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
696 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
697 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
698 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
699 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
700 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
701 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
702 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
703 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
704 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
705 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
706 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
707 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
708 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
709 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
710 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
711 /* End Layer Interface function definitions */
713 VikLayerInterface vik_trw_layer_interface = {
720 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
724 params_groups, /* params_groups */
725 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
729 (VikLayerFuncCreate) trw_layer_create,
730 (VikLayerFuncRealize) trw_layer_realize,
731 (VikLayerFuncPostRead) trw_layer_post_read,
732 (VikLayerFuncFree) trw_layer_free,
734 (VikLayerFuncProperties) NULL,
735 (VikLayerFuncDraw) trw_layer_draw,
736 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
738 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
739 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
741 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
742 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
744 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
745 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
746 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
747 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
748 (VikLayerFuncLayerSelected) trw_layer_selected,
750 (VikLayerFuncMarshall) trw_layer_marshall,
751 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
753 (VikLayerFuncSetParam) trw_layer_set_param,
754 (VikLayerFuncGetParam) trw_layer_get_param,
755 (VikLayerFuncChangeParam) trw_layer_change_param,
757 (VikLayerFuncReadFileData) a_gpspoint_read_file,
758 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
760 (VikLayerFuncDeleteItem) trw_layer_del_item,
761 (VikLayerFuncCutItem) trw_layer_cut_item,
762 (VikLayerFuncCopyItem) trw_layer_copy_item,
763 (VikLayerFuncPasteItem) trw_layer_paste_item,
764 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
766 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
768 (VikLayerFuncSelectClick) trw_layer_select_click,
769 (VikLayerFuncSelectMove) trw_layer_select_move,
770 (VikLayerFuncSelectRelease) trw_layer_select_release,
771 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
774 static gboolean have_diary_program = FALSE;
776 // NB Only performed once per program run
777 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
779 if ( g_find_program_in_path( "rednotebook" ) ) {
780 gchar *stdout = NULL;
781 gchar *stderr = NULL;
782 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
783 if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
784 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
786 g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
788 g_warning ("Diary: stderr: %s", stderr );
790 gchar **tokens = NULL;
791 if ( stdout && g_strcmp0(stdout, "") )
792 tokens = g_strsplit(stdout, " ", 0);
794 tokens = g_strsplit(stderr, " ", 0);
797 gchar *token = tokens[num];
798 while ( token && num < 2 ) {
800 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
801 have_diary_program = TRUE;
806 g_strfreev ( tokens );
813 GType vik_trw_layer_get_type ()
815 static GType vtl_type = 0;
819 static const GTypeInfo vtl_info =
821 sizeof (VikTrwLayerClass),
822 NULL, /* base_init */
823 NULL, /* base_finalize */
824 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
825 NULL, /* class_finalize */
826 NULL, /* class_data */
827 sizeof (VikTrwLayer),
829 NULL /* instance init */
831 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
836 VikTRWMetadata *vik_trw_metadata_new()
838 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
841 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
846 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
848 return vtl->metadata;
851 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
854 vik_trw_metadata_free ( vtl->metadata );
855 vtl->metadata = metadata;
860 const gchar *date_str;
862 const VikWaypoint *wpt;
867 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
871 // Might be an easier way to compare dates rather than converting the strings all the time...
872 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
873 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
875 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
884 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
888 // Might be an easier way to compare dates rather than converting the strings all the time...
889 if ( wpt->has_timestamp ) {
890 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
892 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
902 * Find an item by date
904 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
908 df.date_str = date_str;
913 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
915 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
917 if ( select && df.found ) {
918 if ( do_tracks && df.trk ) {
919 struct LatLon maxmin[2] = { {0,0}, {0,0} };
920 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
921 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
922 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
925 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
926 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
928 vik_layer_emit_update ( VIK_LAYER(vtl) );
933 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
935 static menu_array_sublayer values;
941 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
944 values[MA_VTL] = vtl;
945 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
946 values[MA_SUBLAYER_ID] = sublayer;
947 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
949 trw_layer_delete_item ( values );
952 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
954 static menu_array_sublayer values;
960 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
963 values[MA_VTL] = vtl;
964 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
965 values[MA_SUBLAYER_ID] = sublayer;
966 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
968 trw_layer_copy_item_cb(values);
969 trw_layer_cut_item_cb(values);
972 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
974 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
975 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
976 gpointer * sublayer = values[MA_SUBLAYER_ID];
980 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
984 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
985 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
986 if ( wp && wp->name )
989 name = NULL; // Broken :(
991 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
992 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
993 if ( trk && trk->name )
996 name = NULL; // Broken :(
999 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1000 if ( trk && trk->name )
1003 name = NULL; // Broken :(
1006 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1007 subtype, len, name, data);
1011 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1013 trw_layer_copy_item_cb(values);
1014 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1015 trw_layer_delete_item(values);
1018 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1020 // Slightly cheating method, routing via the panels capability
1021 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1024 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1034 GByteArray *ba = g_byte_array_new ();
1036 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1037 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1038 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1039 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1041 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1044 g_byte_array_append ( ba, id, il );
1052 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1059 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1063 w = vik_waypoint_unmarshall ( item, len );
1064 // When copying - we'll create a new name based on the original
1065 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1066 vik_trw_layer_add_waypoint ( vtl, name, w );
1067 waypoint_convert (NULL, w, &vtl->coord_mode);
1070 trw_layer_calculate_bounds_waypoints ( vtl );
1072 // Consider if redraw necessary for the new item
1073 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1074 vik_layer_emit_update ( VIK_LAYER(vtl) );
1077 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1081 t = vik_track_unmarshall ( item, len );
1082 // When copying - we'll create a new name based on the original
1083 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1084 vik_trw_layer_add_track ( vtl, name, t );
1085 vik_track_convert (t, vtl->coord_mode);
1088 // Consider if redraw necessary for the new item
1089 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1090 vik_layer_emit_update ( VIK_LAYER(vtl) );
1093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1097 t = vik_track_unmarshall ( item, len );
1098 // When copying - we'll create a new name based on the original
1099 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1100 vik_trw_layer_add_route ( vtl, name, t );
1101 vik_track_convert (t, vtl->coord_mode);
1104 // Consider if redraw necessary for the new item
1105 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1106 vik_layer_emit_update ( VIK_LAYER(vtl) );
1112 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1119 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1123 case PARAM_TV: vtl->tracks_visible = data.b; break;
1124 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1125 case PARAM_RV: vtl->routes_visible = data.b; break;
1126 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1127 case PARAM_TLFONTSIZE:
1128 if ( data.u < FS_NUM_SIZES ) {
1129 vtl->track_font_size = data.u;
1130 g_free ( vtl->track_fsize_str );
1131 switch ( vtl->track_font_size ) {
1132 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1133 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1134 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1135 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1136 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1137 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1138 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1142 case PARAM_DM: vtl->drawmode = data.u; break;
1144 vtl->track_color = data.c;
1145 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1147 case PARAM_DP: vtl->drawpoints = data.b; break;
1149 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1150 vtl->drawpoints_size = data.u;
1152 case PARAM_DE: vtl->drawelevation = data.b; break;
1153 case PARAM_DS: vtl->drawstops = data.b; break;
1154 case PARAM_DL: vtl->drawlines = data.b; break;
1155 case PARAM_DD: vtl->drawdirections = data.b; break;
1157 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1158 vtl->drawdirections_size = data.u;
1160 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1161 vtl->stop_length = data.u;
1163 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1164 vtl->elevation_factor = data.u;
1166 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1168 vtl->line_thickness = data.u;
1169 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1172 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1174 vtl->bg_line_thickness = data.u;
1175 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1179 vtl->track_bg_color = data.c;
1180 if ( vtl->track_bg_gc )
1181 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1183 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1184 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1185 case PARAM_DLA: vtl->drawlabels = data.b; break;
1186 case PARAM_DI: vtl->drawimages = data.b; break;
1187 case PARAM_IS: if ( data.u != vtl->image_size )
1189 vtl->image_size = data.u;
1190 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1191 g_queue_free ( vtl->image_cache );
1192 vtl->image_cache = g_queue_new ();
1195 case PARAM_IA: vtl->image_alpha = data.u; break;
1196 case PARAM_ICS: vtl->image_cache_size = data.u;
1197 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1198 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1201 vtl->waypoint_color = data.c;
1202 if ( vtl->waypoint_gc )
1203 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1206 vtl->waypoint_text_color = data.c;
1207 if ( vtl->waypoint_text_gc )
1208 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1211 vtl->waypoint_bg_color = data.c;
1212 if ( vtl->waypoint_bg_gc )
1213 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1216 vtl->wpbgand = data.b;
1217 if ( vtl->waypoint_bg_gc )
1218 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1220 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1221 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1222 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1223 case PARAM_WPFONTSIZE:
1224 if ( data.u < FS_NUM_SIZES ) {
1225 vtl->wp_font_size = data.u;
1226 g_free ( vtl->wp_fsize_str );
1227 switch ( vtl->wp_font_size ) {
1228 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1229 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1230 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1231 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1232 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1233 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1234 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1238 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1240 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1241 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1242 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1243 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1249 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1251 VikLayerParamData rv;
1254 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1255 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1256 case PARAM_RV: rv.b = vtl->routes_visible; break;
1257 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1258 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1259 case PARAM_DM: rv.u = vtl->drawmode; break;
1260 case PARAM_TC: rv.c = vtl->track_color; break;
1261 case PARAM_DP: rv.b = vtl->drawpoints; break;
1262 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1263 case PARAM_DE: rv.b = vtl->drawelevation; break;
1264 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1265 case PARAM_DS: rv.b = vtl->drawstops; break;
1266 case PARAM_SL: rv.u = vtl->stop_length; break;
1267 case PARAM_DL: rv.b = vtl->drawlines; break;
1268 case PARAM_DD: rv.b = vtl->drawdirections; break;
1269 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1270 case PARAM_LT: rv.u = vtl->line_thickness; break;
1271 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1272 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1273 case PARAM_DI: rv.b = vtl->drawimages; break;
1274 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1275 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1276 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1277 case PARAM_IS: rv.u = vtl->image_size; break;
1278 case PARAM_IA: rv.u = vtl->image_alpha; break;
1279 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1280 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1281 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1282 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1283 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1284 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1285 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1286 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1287 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1288 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1290 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1291 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1292 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1293 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1299 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1301 // This '-3' is to account for the first few parameters not in the properties
1302 const gint OFFSET = -3;
1304 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1305 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1308 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1309 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1310 GtkWidget **ww2 = values[UI_CHG_LABELS];
1311 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1312 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1313 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1314 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1315 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1316 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1317 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1318 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1319 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1320 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1321 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1322 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1325 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1328 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1329 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1330 GtkWidget **ww2 = values[UI_CHG_LABELS];
1331 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1332 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1333 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1334 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1335 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1336 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1337 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1338 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1339 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1340 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1341 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1342 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1343 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1344 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1345 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1346 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1349 // Alter sensitivity of all track colours according to the draw track mode.
1352 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1353 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1354 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1355 GtkWidget **ww2 = values[UI_CHG_LABELS];
1356 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1357 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1358 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1359 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1362 case PARAM_MDTIME: {
1363 // Force metadata->timestamp to be always read-only for now.
1364 GtkWidget **ww = values[UI_CHG_WIDGETS];
1365 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1366 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1368 // NB Since other track settings have been split across tabs,
1369 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1374 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1381 // Use byte arrays to store sublayer data
1382 // much like done elsewhere e.g. vik_layer_marshall_params()
1383 GByteArray *ba = g_byte_array_new ( );
1388 guint object_length;
1391 // the length of the item
1392 // the sublayer type of item
1393 // the the actual item
1394 #define tlm_append(object_pointer, size, type) \
1396 object_length = (size); \
1397 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1398 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1399 g_byte_array_append ( ba, (object_pointer), object_length );
1401 // Layer parameters first
1402 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1403 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1404 g_byte_array_append ( ba, pd, pl );
1407 // Now sublayer data
1408 GHashTableIter iter;
1409 gpointer key, value;
1412 g_hash_table_iter_init ( &iter, vtl->waypoints );
1413 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1414 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1415 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1420 g_hash_table_iter_init ( &iter, vtl->tracks );
1421 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1422 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1423 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1428 g_hash_table_iter_init ( &iter, vtl->routes );
1429 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1430 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1431 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1441 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1443 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1445 gint consumed_length;
1447 // First the overall layer parameters
1448 memcpy(&pl, data, sizeof(pl));
1450 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1453 consumed_length = pl;
1454 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1456 #define tlm_size (*(gint *)data)
1457 // See marshalling above for order of how this is written
1459 data += sizeof_len_and_subtype + tlm_size;
1461 // Now the individual sublayers:
1463 while ( *data && consumed_length < len ) {
1464 // Normally four extra bytes at the end of the datastream
1465 // (since it's a GByteArray and that's where it's length is stored)
1466 // So only attempt read when there's an actual block of sublayer data
1467 if ( consumed_length + tlm_size < len ) {
1469 // Reuse pl to read the subtype from the data stream
1470 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1472 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1473 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1474 gchar *name = g_strdup ( trk->name );
1475 vik_trw_layer_add_track ( vtl, name, trk );
1478 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1479 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1480 gchar *name = g_strdup ( wp->name );
1481 vik_trw_layer_add_waypoint ( vtl, name, wp );
1484 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1485 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1486 gchar *name = g_strdup ( trk->name );
1487 vik_trw_layer_add_route ( vtl, name, trk );
1491 consumed_length += tlm_size + sizeof_len_and_subtype;
1494 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1496 // Not stored anywhere else so need to regenerate
1497 trw_layer_calculate_bounds_waypoints ( vtl );
1502 // Keep interesting hash function at least visible
1504 static guint strcase_hash(gconstpointer v)
1506 // 31 bit hash function
1509 gchar s[128]; // malloc is too slow for reading big files
1512 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1513 p[i] = toupper(t[i]);
1519 for (p += 1; *p != '\0'; p++)
1520 h = (h << 5) - h + *p;
1527 // Stick a 1 at the end of the function name to make it more unique
1528 // thus more easily searchable in a simple text editor
1529 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1531 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1532 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1534 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1535 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1537 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1538 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1539 // and with normal PC processing capabilities - it has negligibile performance impact
1540 // This also minimized the amount of rework - as the management of the hash tables already exists.
1542 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1543 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1544 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1546 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1547 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1548 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1549 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1550 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1551 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1553 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1555 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1557 // Param settings that are not available via the GUI
1558 // Force to on after processing params (which defaults them to off with a zero value)
1559 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1561 rv->metadata = vik_trw_metadata_new ();
1562 rv->draw_sync_done = TRUE;
1563 rv->draw_sync_do = TRUE;
1564 // Everything else is 0, FALSE or NULL
1570 static void trw_layer_free ( VikTrwLayer *trwlayer )
1572 g_hash_table_destroy(trwlayer->waypoints);
1573 g_hash_table_destroy(trwlayer->waypoints_iters);
1574 g_hash_table_destroy(trwlayer->tracks);
1575 g_hash_table_destroy(trwlayer->tracks_iters);
1576 g_hash_table_destroy(trwlayer->routes);
1577 g_hash_table_destroy(trwlayer->routes_iters);
1579 /* ODC: replace with GArray */
1580 trw_layer_free_track_gcs ( trwlayer );
1582 if ( trwlayer->wp_right_click_menu )
1583 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1585 if ( trwlayer->track_right_click_menu )
1586 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1588 if ( trwlayer->tracklabellayout != NULL)
1589 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1591 if ( trwlayer->wplabellayout != NULL)
1592 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1594 if ( trwlayer->waypoint_gc != NULL )
1595 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1597 if ( trwlayer->waypoint_text_gc != NULL )
1598 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1600 if ( trwlayer->waypoint_bg_gc != NULL )
1601 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1603 g_free ( trwlayer->wp_fsize_str );
1604 g_free ( trwlayer->track_fsize_str );
1606 if ( trwlayer->tpwin != NULL )
1607 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1609 if ( trwlayer->tracks_analysis_dialog != NULL )
1610 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1612 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1613 g_queue_free ( trwlayer->image_cache );
1616 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1620 dp->highlight = highlight;
1621 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1622 dp->xmpp = vik_viewport_get_xmpp ( vp );
1623 dp->ympp = vik_viewport_get_ympp ( vp );
1624 dp->width = vik_viewport_get_width ( vp );
1625 dp->height = vik_viewport_get_height ( vp );
1626 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1627 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1629 dp->center = vik_viewport_get_center ( vp );
1630 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1631 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1636 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1637 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1638 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1640 dp->ce1 = dp->center->east_west-w2;
1641 dp->ce2 = dp->center->east_west+w2;
1642 dp->cn1 = dp->center->north_south-h2;
1643 dp->cn2 = dp->center->north_south+h2;
1644 } else if ( dp->lat_lon ) {
1645 VikCoord upperleft, bottomright;
1646 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1647 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1648 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1649 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1650 dp->ce1 = upperleft.east_west;
1651 dp->ce2 = bottomright.east_west;
1652 dp->cn1 = bottomright.north_south;
1653 dp->cn2 = upperleft.north_south;
1656 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1660 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1661 * Here a simple traffic like light colour system is used:
1662 * . slow points are red
1663 * . average is yellow
1664 * . fast points are green
1666 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1669 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1670 if ( average_speed > 0 ) {
1671 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1672 if ( rv < low_speed )
1673 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1674 else if ( rv > high_speed )
1675 return VIK_TRW_LAYER_TRACK_GC_FAST;
1677 return VIK_TRW_LAYER_TRACK_GC_AVER;
1680 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1683 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1685 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1686 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1687 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1688 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1692 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1694 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1696 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1697 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1699 // Fallback if parse failure
1700 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1702 g_free ( label_markup );
1704 gint label_x, label_y;
1706 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1708 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1709 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1713 * distance_in_preferred_units:
1714 * @dist: The source distance in standard SI Units (i.e. metres)
1716 * TODO: This is a generic function that could be moved into globals.c or utils.c
1718 * Probably best used if you have a only few conversions to perform.
1719 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1720 * since it will be doing the preference check on each call
1722 * Returns: The distance in the units as specified by the preferences
1724 static gdouble distance_in_preferred_units ( gdouble dist )
1727 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1728 switch (dist_units) {
1729 case VIK_UNITS_DISTANCE_MILES:
1730 mydist = VIK_METERS_TO_MILES(dist);
1732 // VIK_UNITS_DISTANCE_KILOMETRES:
1734 mydist = dist/1000.0;
1741 * trw_layer_draw_dist_labels:
1743 * Draw a few labels along a track at nicely seperated distances
1744 * This might slow things down if there's many tracks being displayed with this on.
1746 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1748 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1749 25.0, 40.0, 50.0, 75.0, 100.0,
1750 150.0, 200.0, 250.0, 500.0, 1000.0};
1752 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1754 // Convert to specified unit to find the friendly breakdown value
1755 dist = distance_in_preferred_units ( dist );
1759 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1760 if ( chunksd[i] > dist ) {
1762 dist = chunksd[index];
1767 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1769 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1770 gdouble dist_i = dist * i;
1772 // Convert distance back into metres for use in finding a trackpoint
1773 switch (dist_units) {
1774 case VIK_UNITS_DISTANCE_MILES:
1775 dist_i = VIK_MILES_TO_METERS(dist_i);
1777 // VIK_UNITS_DISTANCE_KILOMETRES:
1779 dist_i = dist_i*1000.0;
1783 gdouble dist_current = 0.0;
1784 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1785 gdouble dist_next = 0.0;
1786 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1788 gdouble dist_between_tps = fabs (dist_next - dist_current);
1789 gdouble ratio = 0.0;
1790 // Prevent division by 0 errors
1791 if ( dist_between_tps > 0.0 )
1792 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1794 if ( tp_current && tp_next ) {
1795 // Construct the name based on the distance value
1798 switch (dist_units) {
1799 case VIK_UNITS_DISTANCE_MILES:
1800 units = g_strdup ( _("miles") );
1802 // VIK_UNITS_DISTANCE_KILOMETRES:
1804 units = g_strdup ( _("km") );
1808 // Convert for display
1809 dist_i = distance_in_preferred_units ( dist_i );
1811 // Make the precision of the output related to the unit size.
1813 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1814 else if ( index == 1 )
1815 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1817 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1820 struct LatLon ll_current, ll_next;
1821 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1822 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1824 // positional interpolation
1825 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1826 // but should be good enough over the small scale that I anticipate usage on
1827 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1828 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1830 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1833 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1834 fgcolour = gdk_color_to_string ( &(trk->color) );
1836 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1838 // if highlight mode on, then colour the background in the highlight colour
1840 if ( drawing_highlight )
1841 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1843 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1845 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1847 g_free ( fgcolour );
1848 g_free ( bgcolour );
1855 * trw_layer_draw_track_name_labels:
1857 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1859 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1862 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1863 fgcolour = gdk_color_to_string ( &(trk->color) );
1865 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1867 // if highlight mode on, then colour the background in the highlight colour
1869 if ( drawing_highlight )
1870 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1872 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1874 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1876 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1877 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1878 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1879 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1880 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1881 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1883 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1885 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1888 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1889 // No other labels to draw
1892 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1895 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1898 VikCoord begin_coord = tp_begin->coord;
1899 VikCoord end_coord = tp_end->coord;
1901 gboolean done_start_end = FALSE;
1903 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1904 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1906 // This number can be configured via the settings if you really want to change it
1907 gdouble distance_diff;
1908 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1909 distance_diff = 100.0; // Metres
1911 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1912 // Start and end 'close' together so only draw one label at an average location
1913 gint x1, x2, y1, y2;
1914 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1915 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1917 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1919 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1920 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1923 done_start_end = TRUE;
1927 if ( ! done_start_end ) {
1928 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1929 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1930 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1931 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1932 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1933 g_free ( name_start );
1935 // Don't draw end label if this is the one being created
1936 if ( trk != dp->vtl->current_track ) {
1937 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1938 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1939 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1940 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1941 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1942 g_free ( name_end );
1947 g_free ( fgcolour );
1948 g_free ( bgcolour );
1952 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1954 if ( ! track->visible )
1957 /* TODO: this function is a mess, get rid of any redundancy */
1958 GList *list = track->trackpoints;
1960 gboolean useoldvals = TRUE;
1962 gboolean drawpoints;
1964 gboolean drawelevation;
1965 gdouble min_alt, max_alt, alt_diff = 0;
1967 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1968 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1971 if ( dp->vtl->drawelevation )
1973 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1974 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1975 alt_diff = max_alt - min_alt;
1978 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1979 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1980 trw_layer_draw_track ( id, track, dp, TRUE );
1982 if ( draw_track_outline )
1983 drawpoints = drawstops = FALSE;
1985 drawpoints = dp->vtl->drawpoints;
1986 drawstops = dp->vtl->drawstops;
1989 gboolean drawing_highlight = FALSE;
1990 /* Current track - used for creation */
1991 if ( track == dp->vtl->current_track )
1992 main_gc = dp->vtl->current_track_gc;
1994 if ( dp->highlight ) {
1995 /* Draw all tracks of the layer in special colour
1996 NB this supercedes the drawmode */
1997 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1998 drawing_highlight = TRUE;
2000 if ( !drawing_highlight ) {
2001 // Still need to figure out the gc according to the drawing mode:
2002 switch ( dp->vtl->drawmode ) {
2003 case DRAWMODE_BY_TRACK:
2004 if ( dp->vtl->track_1color_gc )
2005 g_object_unref ( dp->vtl->track_1color_gc );
2006 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2007 main_gc = dp->vtl->track_1color_gc;
2010 // Mostly for DRAWMODE_ALL_SAME_COLOR
2011 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2012 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2019 int x, y, oldx, oldy;
2020 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2022 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2024 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2026 // Draw the first point as something a bit different from the normal points
2027 // ATM it's slightly bigger and a triangle
2029 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2030 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2036 gdouble average_speed = 0.0;
2037 gdouble low_speed = 0.0;
2038 gdouble high_speed = 0.0;
2039 // If necessary calculate these values - which is done only once per track redraw
2040 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2041 // the percentage factor away from the average speed determines transistions between the levels
2042 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2043 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2044 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2047 while ((list = g_list_next(list)))
2049 tp = VIK_TRACKPOINT(list->data);
2050 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2052 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2053 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2054 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2055 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2056 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2058 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2061 * If points are the same in display coordinates, don't draw.
2063 if ( useoldvals && x == oldx && y == oldy )
2065 // Still need to process points to ensure 'stops' are drawn if required
2066 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2067 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2068 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 );
2073 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2074 if ( drawpoints || dp->vtl->drawlines ) {
2075 // setup main_gc for both point and line drawing
2076 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2077 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 ) );
2081 if ( drawpoints && ! draw_track_outline )
2086 * The concept of drawing stops is that a trackpoint
2087 * that is if the next trackpoint has a timestamp far into
2088 * the future, we draw a circle of 6x trackpoint size,
2089 * instead of a rectangle of 2x trackpoint size.
2090 * This is drawn first so the trackpoint will be drawn on top
2093 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2094 /* Stop point. Draw 6x circle. Always in redish colour */
2095 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 );
2097 /* Regular point - draw 2x square. */
2098 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2101 /* Final point - draw 4x circle. */
2102 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 );
2105 if ((!tp->newsegment) && (dp->vtl->drawlines))
2108 /* UTM only: zone check */
2109 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2110 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2113 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2115 if ( draw_track_outline ) {
2116 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2120 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2122 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2124 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2129 tmp[1].y = oldy-FIXALTITUDE(list->data);
2131 tmp[2].y = y-FIXALTITUDE(list->next->data);
2136 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2137 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2139 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2140 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2142 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2147 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2148 // Draw an arrow at the mid point to show the direction of the track
2149 // Code is a rework from vikwindow::draw_ruler()
2150 gint midx = (oldx + x) / 2;
2151 gint midy = (oldy + y) / 2;
2153 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2154 // Avoid divide by zero and ensure at least 1 pixel big
2156 gdouble dx = (oldx - midx) / len;
2157 gdouble dy = (oldy - midy) / len;
2158 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2159 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 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2171 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2172 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2174 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2176 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2177 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 ));
2181 * If points are the same in display coordinates, don't draw.
2183 if ( x != oldx || y != oldy )
2185 if ( draw_track_outline )
2186 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2188 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2194 * If points are the same in display coordinates, don't draw.
2196 if ( x != oldx && y != oldy )
2198 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2199 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2207 // Labels drawn after the trackpoints, so the labels are on top
2208 if ( dp->vtl->track_draw_labels ) {
2209 if ( track->max_number_dist_labels > 0 ) {
2210 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2213 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2214 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2220 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2222 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2223 trw_layer_draw_track ( id, track, dp, FALSE );
2227 static void cached_pixbuf_free ( CachedPixbuf *cp )
2229 g_object_unref ( G_OBJECT(cp->pixbuf) );
2230 g_free ( cp->image );
2233 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2235 return strcmp ( cp->image, name );
2238 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2241 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2242 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2243 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2246 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2248 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2250 if ( wp->image && dp->vtl->drawimages )
2252 GdkPixbuf *pixbuf = NULL;
2255 if ( dp->vtl->image_alpha == 0)
2258 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2260 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2263 gchar *image = wp->image;
2264 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2265 if ( ! regularthumb )
2267 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2268 image = "\x12\x00"; /* this shouldn't occur naturally. */
2272 CachedPixbuf *cp = NULL;
2273 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2274 if ( dp->vtl->image_size == 128 )
2275 cp->pixbuf = regularthumb;
2278 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2279 g_assert ( cp->pixbuf );
2280 g_object_unref ( G_OBJECT(regularthumb) );
2282 cp->image = g_strdup ( image );
2284 /* needed so 'click picture' tool knows how big the pic is; we don't
2285 * store it in cp because they may have been freed already. */
2286 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2287 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2289 g_queue_push_head ( dp->vtl->image_cache, cp );
2290 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2291 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2293 pixbuf = cp->pixbuf;
2297 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2303 w = gdk_pixbuf_get_width ( pixbuf );
2304 h = gdk_pixbuf_get_height ( pixbuf );
2306 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2308 if ( dp->highlight ) {
2309 // Highlighted - so draw a little border around the chosen one
2310 // single line seems a little weak so draw 2 of them
2311 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2312 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2313 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2314 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2317 if ( dp->vtl->image_alpha == 255 )
2318 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2320 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2322 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2326 // Draw appropriate symbol - either symbol image or simple types
2327 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2328 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 );
2330 else if ( wp == dp->vtl->current_wp ) {
2331 switch ( dp->vtl->wp_symbol ) {
2332 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;
2333 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;
2334 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;
2335 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 );
2336 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 );
2340 switch ( dp->vtl->wp_symbol ) {
2341 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;
2342 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;
2343 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;
2344 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 );
2345 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;
2349 if ( dp->vtl->drawlabels )
2351 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2352 gint label_x, label_y;
2354 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2356 // Could this stored in the waypoint rather than recreating each pass?
2357 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2359 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2360 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2362 // Fallback if parse failure
2363 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2365 g_free ( wp_label_markup );
2367 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2368 label_x = x - width/2;
2369 if ( wp->symbol_pixbuf )
2370 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2372 label_y = y - dp->vtl->wp_size - height - 2;
2374 /* if highlight mode on, then draw background text in highlight colour */
2375 if ( dp->highlight )
2376 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2378 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2379 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2384 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2386 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2387 trw_layer_draw_waypoint ( id, wp, dp );
2391 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2393 static struct DrawingParams dp;
2394 g_assert ( l != NULL );
2396 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2398 if ( l->tracks_visible )
2399 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2401 if ( l->routes_visible )
2402 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2404 if (l->waypoints_visible)
2405 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2408 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2410 // 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
2411 // This may seem slightly inefficient to test each time for every layer
2412 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2413 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2414 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2416 trw_layer_draw_with_highlight ( l, data, FALSE );
2419 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2421 // Check the layer for visibility (including all the parents visibilities)
2422 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2424 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2428 * vik_trw_layer_draw_highlight_item:
2430 * Only handles a single track or waypoint ATM
2431 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2433 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2435 // Check the layer for visibility (including all the parents visibilities)
2436 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2439 static struct DrawingParams dp;
2440 init_drawing_params ( &dp, vtl, vvp, TRUE );
2443 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2445 trw_layer_draw_track_cb ( NULL, trk, &dp );
2447 if ( vtl->waypoints_visible && wpt ) {
2448 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2453 * vik_trw_layer_draw_highlight_item:
2455 * Generally for drawing all tracks or routes or waypoints
2456 * trks may be actually routes
2457 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2459 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2461 // Check the layer for visibility (including all the parents visibilities)
2462 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2465 static struct DrawingParams dp;
2466 init_drawing_params ( &dp, vtl, vvp, TRUE );
2469 gboolean is_routes = (trks == vtl->routes);
2470 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2472 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2475 if ( vtl->waypoints_visible && wpts )
2476 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2479 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2482 if ( vtl->track_bg_gc )
2484 g_object_unref ( vtl->track_bg_gc );
2485 vtl->track_bg_gc = NULL;
2487 if ( vtl->track_1color_gc )
2489 g_object_unref ( vtl->track_1color_gc );
2490 vtl->track_1color_gc = NULL;
2492 if ( vtl->current_track_gc )
2494 g_object_unref ( vtl->current_track_gc );
2495 vtl->current_track_gc = NULL;
2497 if ( vtl->current_track_newpoint_gc )
2499 g_object_unref ( vtl->current_track_newpoint_gc );
2500 vtl->current_track_newpoint_gc = NULL;
2503 if ( ! vtl->track_gc )
2505 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2506 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2507 g_array_free ( vtl->track_gc, TRUE );
2508 vtl->track_gc = NULL;
2511 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2513 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2514 gint width = vtl->line_thickness;
2516 if ( vtl->track_gc )
2517 trw_layer_free_track_gcs ( vtl );
2519 if ( vtl->track_bg_gc )
2520 g_object_unref ( vtl->track_bg_gc );
2521 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2523 // Ensure new track drawing heeds line thickness setting
2524 // however always have a minium of 2, as 1 pixel is really narrow
2525 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2527 if ( vtl->current_track_gc )
2528 g_object_unref ( vtl->current_track_gc );
2529 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2530 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2532 // 'newpoint' gc is exactly the same as the current track gc
2533 if ( vtl->current_track_newpoint_gc )
2534 g_object_unref ( vtl->current_track_newpoint_gc );
2535 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2536 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2538 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2540 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2541 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2543 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2544 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2545 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2547 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2549 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2552 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2554 VikTrwLayer *rv = trw_layer_new1 ( vp );
2555 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2557 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2558 /* early exit, as the rest is GUI related */
2562 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2563 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2565 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2566 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2568 trw_layer_new_track_gcs ( rv, vp );
2570 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2571 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2572 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2573 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2575 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2577 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2582 #define SMALL_ICON_SIZE 18
2584 * Can accept a null symbol, and may return null value
2586 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2588 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2589 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2590 // So needing a small icon for the treeview may need some resizing:
2591 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2592 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2596 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2598 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2600 GdkPixbuf *pixbuf = NULL;
2602 if ( track->has_color ) {
2603 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2604 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2605 // Here is some magic found to do the conversion
2606 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2607 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2608 ((track->color.green & 0xff00) << 8) |
2609 (track->color.blue & 0xff00);
2611 gdk_pixbuf_fill ( pixbuf, pixel );
2614 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 );
2617 g_object_unref (pixbuf);
2619 *new_iter = *((GtkTreeIter *) pass_along[1]);
2620 if ( track->is_route )
2621 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2623 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2625 if ( ! track->visible )
2626 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2629 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2631 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2633 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 );
2635 *new_iter = *((GtkTreeIter *) pass_along[1]);
2636 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2638 if ( ! wp->visible )
2639 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2642 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2644 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2647 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2649 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2652 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2654 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2657 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2660 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2662 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2663 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2665 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2667 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2670 if ( g_hash_table_size (vtl->routes) > 0 ) {
2671 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2673 pass_along[0] = &(vtl->routes_iter);
2674 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2676 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2678 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2681 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2682 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2684 pass_along[0] = &(vtl->waypoints_iter);
2685 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2687 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2689 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2694 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2698 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2699 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2700 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2701 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2703 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2705 return (t->visible ^= 1);
2709 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2711 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2713 return (t->visible ^= 1);
2717 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2719 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2721 return (t->visible ^= 1);
2730 * Return a property about tracks for this layer
2732 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2734 return vtl->line_thickness;
2737 // Structure to hold multiple track information for a layer
2746 * Build up layer multiple track information via updating the tooltip_tracks structure
2748 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2750 tt->length = tt->length + vik_track_get_length (tr);
2752 // Ensure times are available
2753 if ( tr->trackpoints &&
2754 vik_track_get_tp_first(tr)->has_timestamp &&
2755 vik_track_get_tp_last(tr)->has_timestamp ) {
2758 t1 = vik_track_get_tp_first(tr)->timestamp;
2759 t2 = vik_track_get_tp_last(tr)->timestamp;
2761 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2762 // Hence initialize to the first 'proper' value
2763 if ( tt->start_time == 0 )
2764 tt->start_time = t1;
2765 if ( tt->end_time == 0 )
2768 // Update find the earliest / last times
2769 if ( t1 < tt->start_time )
2770 tt->start_time = t1;
2771 if ( t2 > tt->end_time )
2774 // Keep track of total time
2775 // there maybe gaps within a track (eg segments)
2776 // but this should be generally good enough for a simple indicator
2777 tt->duration = tt->duration + (int)(t2-t1);
2782 * Generate tooltip text for the layer.
2783 * This is relatively complicated as it considers information for
2784 * no tracks, a single track or multiple tracks
2785 * (which may or may not have timing information)
2787 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2798 static gchar tmp_buf[128];
2801 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2803 // Safety check - I think these should always be valid
2804 if ( vtl->tracks && vtl->waypoints ) {
2805 tooltip_tracks tt = { 0.0, 0, 0 };
2806 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2808 GDate* gdate_start = g_date_new ();
2809 g_date_set_time_t (gdate_start, tt.start_time);
2811 GDate* gdate_end = g_date_new ();
2812 g_date_set_time_t (gdate_end, tt.end_time);
2814 if ( g_date_compare (gdate_start, gdate_end) ) {
2815 // Dates differ so print range on separate line
2816 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2817 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2818 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2821 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2822 if ( tt.start_time != 0 )
2823 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2827 if ( tt.length > 0.0 ) {
2828 gdouble len_in_units;
2830 // Setup info dependent on distance units
2831 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2832 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2833 len_in_units = VIK_METERS_TO_MILES(tt.length);
2836 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2837 len_in_units = tt.length/1000.0;
2840 // Timing information if available
2842 if ( tt.duration > 0 ) {
2843 g_snprintf (tbuf1, sizeof(tbuf1),
2844 _(" in %d:%02d hrs:mins"),
2845 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2847 g_snprintf (tbuf2, sizeof(tbuf2),
2848 _("\n%sTotal Length %.1f %s%s"),
2849 tbuf3, len_in_units, tbuf4, tbuf1);
2852 // Put together all the elements to form compact tooltip text
2853 g_snprintf (tmp_buf, sizeof(tmp_buf),
2854 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2855 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2857 g_date_free (gdate_start);
2858 g_date_free (gdate_end);
2865 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2869 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2871 // Very simple tooltip - may expand detail in the future...
2872 static gchar tmp_buf[32];
2873 g_snprintf (tmp_buf, sizeof(tmp_buf),
2875 g_hash_table_size (l->tracks));
2879 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2881 // Very simple tooltip - may expand detail in the future...
2882 static gchar tmp_buf[32];
2883 g_snprintf (tmp_buf, sizeof(tmp_buf),
2885 g_hash_table_size (l->routes));
2890 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2891 // Same tooltip for a route
2892 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2895 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2896 tr = g_hash_table_lookup ( l->tracks, sublayer );
2898 tr = g_hash_table_lookup ( l->routes, sublayer );
2901 // Could be a better way of handling strings - but this works...
2902 gchar time_buf1[20];
2903 gchar time_buf2[20];
2904 time_buf1[0] = '\0';
2905 time_buf2[0] = '\0';
2906 static gchar tmp_buf[100];
2907 // Compact info: Short date eg (11/20/99), duration and length
2908 // Hopefully these are the things that are most useful and so promoted into the tooltip
2909 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2910 // %x The preferred date representation for the current locale without the time.
2911 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2912 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2913 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2915 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2918 // Get length and consider the appropriate distance units
2919 gdouble tr_len = vik_track_get_length(tr);
2920 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2921 switch (dist_units) {
2922 case VIK_UNITS_DISTANCE_KILOMETRES:
2923 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2925 case VIK_UNITS_DISTANCE_MILES:
2926 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2935 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2937 // Very simple tooltip - may expand detail in the future...
2938 static gchar tmp_buf[32];
2939 g_snprintf (tmp_buf, sizeof(tmp_buf),
2941 g_hash_table_size (l->waypoints));
2945 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2947 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2948 // NB It's OK to return NULL
2953 return w->description;
2962 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2965 * set_statusbar_msg_info_trkpt:
2967 * Function to show track point information on the statusbar
2968 * Items displayed is controlled by the settings format code
2970 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2972 gchar *statusbar_format_code = NULL;
2973 gboolean need2free = FALSE;
2974 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2975 // Otherwise use default
2976 statusbar_format_code = g_strdup ( "KEATDN" );
2980 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2981 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2985 g_free ( statusbar_format_code );
2989 * Function to show basic waypoint information on the statusbar
2991 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2994 switch (a_vik_get_units_height ()) {
2995 case VIK_UNITS_HEIGHT_FEET:
2996 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2999 //VIK_UNITS_HEIGHT_METRES:
3000 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3004 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3005 // one can easily use the current pointer position to see this if needed
3006 gchar *lat = NULL, *lon = NULL;
3007 static struct LatLon ll;
3008 vik_coord_to_latlon (&(wpt->coord), &ll);
3009 a_coords_latlon_to_string ( &ll, &lat, &lon );
3011 // Combine parts to make overall message
3014 // Add comment if available
3015 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3017 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3018 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3025 * General layer selection function, find out which bit is selected and take appropriate action
3027 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3030 l->current_wp = NULL;
3031 l->current_wp_id = NULL;
3032 trw_layer_cancel_current_tp ( l, FALSE );
3035 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3039 case VIK_TREEVIEW_TYPE_LAYER:
3041 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3042 /* Mark for redraw */
3047 case VIK_TREEVIEW_TYPE_SUBLAYER:
3051 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3053 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3054 /* Mark for redraw */
3058 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3060 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3061 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3062 /* Mark for redraw */
3066 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3068 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3069 /* Mark for redraw */
3073 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3075 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3076 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3077 /* Mark for redraw */
3081 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3083 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3084 /* Mark for redraw */
3088 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3090 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3092 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3093 // Show some waypoint info
3094 set_statusbar_msg_info_wpt ( l, wpt );
3095 /* Mark for redraw */
3102 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3111 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3116 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3121 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3126 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3128 return l->waypoints;
3131 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3133 return vtl->tracks_iters;
3136 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3138 return vtl->routes_iters;
3141 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3143 return vtl->waypoints;
3146 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3148 return ! ( g_hash_table_size ( vtl->tracks ) ||
3149 g_hash_table_size ( vtl->routes ) ||
3150 g_hash_table_size ( vtl->waypoints ) );
3153 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3155 return vtl->tracks_visible;
3158 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3160 return vtl->routes_visible;
3163 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3165 return vtl->waypoints_visible;
3169 * ATM use a case sensitive find
3170 * Finds the first one
3172 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3174 if ( wp && wp->name )
3175 if ( ! strcmp ( wp->name, name ) )
3181 * Get waypoint by name - not guaranteed to be unique
3182 * Finds the first one
3184 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3186 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3190 * ATM use a case sensitive find
3191 * Finds the first one
3193 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3195 if ( trk && trk->name )
3196 if ( ! strcmp ( trk->name, name ) )
3202 * Get track by name - not guaranteed to be unique
3203 * Finds the first one
3205 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3207 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3211 * Get route by name - not guaranteed to be unique
3212 * Finds the first one
3214 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3216 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3219 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3221 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3222 maxmin[0].lat = trk->bbox.north;
3223 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3224 maxmin[1].lat = trk->bbox.south;
3225 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3226 maxmin[0].lon = trk->bbox.east;
3227 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3228 maxmin[1].lon = trk->bbox.west;
3231 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3233 // Continually reuse maxmin to find the latest maximum and minimum values
3234 // First set to waypoints bounds
3235 maxmin[0].lat = vtl->waypoints_bbox.north;
3236 maxmin[1].lat = vtl->waypoints_bbox.south;
3237 maxmin[0].lon = vtl->waypoints_bbox.east;
3238 maxmin[1].lon = vtl->waypoints_bbox.west;
3239 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3240 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3243 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3245 /* 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... */
3246 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3247 trw_layer_find_maxmin (vtl, maxmin);
3248 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3252 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3253 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3258 static void trw_layer_centerize ( menu_array_layer values )
3260 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3262 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3263 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3265 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3268 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3270 /* First set the center [in case previously viewing from elsewhere] */
3271 /* Then loop through zoom levels until provided positions are in view */
3272 /* This method is not particularly fast - but should work well enough */
3273 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3275 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3276 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3278 /* Convert into definite 'smallest' and 'largest' positions */
3279 struct LatLon minmin;
3280 if ( maxmin[0].lat < maxmin[1].lat )
3281 minmin.lat = maxmin[0].lat;
3283 minmin.lat = maxmin[1].lat;
3285 struct LatLon maxmax;
3286 if ( maxmin[0].lon > maxmin[1].lon )
3287 maxmax.lon = maxmin[0].lon;
3289 maxmax.lon = maxmin[1].lon;
3291 /* Never zoom in too far - generally not that useful, as too close ! */
3292 /* Always recalculate the 'best' zoom level */
3294 vik_viewport_set_zoom ( vvp, zoom );
3296 gdouble min_lat, max_lat, min_lon, max_lon;
3297 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3298 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3299 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3300 /* NB I think the logic used in this test to determine if the bounds is within view
3301 fails if track goes across 180 degrees longitude.
3302 Hopefully that situation is not too common...
3303 Mind you viking doesn't really do edge locations to well anyway */
3304 if ( min_lat < minmin.lat &&
3305 max_lat > minmin.lat &&
3306 min_lon < maxmax.lon &&
3307 max_lon > maxmax.lon )
3308 /* Found within zoom level */
3313 vik_viewport_set_zoom ( vvp, zoom );
3317 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3319 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3320 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3321 trw_layer_find_maxmin (vtl, maxmin);
3322 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3325 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3330 static void trw_layer_auto_view ( menu_array_layer values )
3332 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3333 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3334 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3335 vik_layers_panel_emit_update ( vlp );
3338 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3341 static void trw_layer_export_gpspoint ( menu_array_layer values )
3343 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3345 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3347 g_free ( auto_save_name );
3350 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3352 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3354 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3356 g_free ( auto_save_name );
3359 static void trw_layer_export_gpx ( menu_array_layer values )
3361 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3363 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3365 g_free ( auto_save_name );
3368 static void trw_layer_export_kml ( menu_array_layer values )
3370 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3372 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3374 g_free ( auto_save_name );
3377 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3379 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3380 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3383 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3385 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3388 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3390 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3393 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3395 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3397 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3398 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3400 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3402 if ( !trk || !trk->name )
3405 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3407 gchar *label = NULL;
3408 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3409 label = _("Export Route as GPX");
3411 label = _("Export Track as GPX");
3412 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3414 g_free ( auto_save_name );
3417 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3419 wpu_udata *user_data = udata;
3420 if ( wp == user_data->wp ) {
3421 user_data->uuid = id;
3427 static void trw_layer_goto_wp ( menu_array_layer values )
3429 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3430 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3431 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3432 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3433 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3435 GTK_RESPONSE_REJECT,
3437 GTK_RESPONSE_ACCEPT,
3440 GtkWidget *label, *entry;
3441 label = gtk_label_new(_("Waypoint Name:"));
3442 entry = gtk_entry_new();
3444 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3445 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3446 gtk_widget_show_all ( label );
3447 gtk_widget_show_all ( entry );
3449 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3451 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3453 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3454 // Find *first* wp with the given name
3455 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3458 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3461 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3462 vik_layers_panel_emit_update ( vlp );
3464 // Find and select on the side panel
3469 // Hmmm, want key of it
3470 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3472 if ( wpf && udata.uuid ) {
3473 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3474 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3483 gtk_widget_destroy ( dia );
3486 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3488 gchar *default_name = highest_wp_number_get(vtl);
3489 VikWaypoint *wp = vik_waypoint_new();
3490 gchar *returned_name;
3492 wp->coord = *def_coord;
3494 // Attempt to auto set height if DEM data is available
3495 vik_waypoint_apply_dem_data ( wp, TRUE );
3497 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3499 if ( returned_name )
3502 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3503 g_free (default_name);
3504 g_free (returned_name);
3507 g_free (default_name);
3508 vik_waypoint_free(wp);
3512 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3514 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3515 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3516 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3517 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3518 VikViewport *vvp = vik_window_viewport(vw);
3520 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3521 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3522 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3523 trw_layer_calculate_bounds_waypoints ( vtl );
3524 vik_layers_panel_emit_update ( vlp );
3527 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3529 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3530 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3531 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3533 trw_layer_find_maxmin (vtl, maxmin);
3534 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3535 trw_layer_calculate_bounds_waypoints ( vtl );
3536 vik_layers_panel_emit_update ( vlp );
3539 #ifdef VIK_CONFIG_GEOTAG
3540 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3542 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3544 // Update directly - not changing the mtime
3545 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3548 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3550 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3553 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3557 * Use code in separate file for this feature as reasonably complex
3559 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3561 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3562 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3563 // Unset so can be reverified later if necessary
3564 vtl->has_verified_thumbnails = FALSE;
3566 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3572 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3574 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3575 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3577 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3583 static void trw_layer_geotagging ( menu_array_layer values )
3585 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3586 // Unset so can be reverified later if necessary
3587 vtl->has_verified_thumbnails = FALSE;
3589 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3596 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3598 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3600 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3601 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3602 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3603 VikViewport *vvp = vik_window_viewport(vw);
3605 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3609 * Acquire into this TRW Layer straight from GPS Device
3611 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3613 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3614 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3618 * Acquire into this TRW Layer from Directions
3620 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3622 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3626 * Acquire into this TRW Layer from an entered URL
3628 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3630 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3631 trw_layer_acquire ( values, &vik_datasource_url_interface );
3634 #ifdef VIK_CONFIG_OPENSTREETMAP
3636 * Acquire into this TRW Layer from OSM
3638 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3640 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3644 * Acquire into this TRW Layer from OSM for 'My' Traces
3646 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3648 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3652 #ifdef VIK_CONFIG_GEOCACHES
3654 * Acquire into this TRW Layer from Geocaching.com
3656 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3658 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3662 #ifdef VIK_CONFIG_GEOTAG
3664 * Acquire into this TRW Layer from images
3666 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3668 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3670 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3671 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3673 // Reverify thumbnails as they may have changed
3674 vtl->has_verified_thumbnails = FALSE;
3675 trw_layer_verify_thumbnails ( vtl, NULL );
3679 static void trw_layer_gps_upload ( menu_array_layer values )
3681 menu_array_sublayer data;
3683 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3685 data[MA_VTL] = values[MA_VTL];
3686 data[MA_VLP] = values[MA_VLP];
3688 trw_layer_gps_upload_any ( data );
3692 * If pass_along[3] is defined that this will upload just that track
3694 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3696 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3697 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3699 // May not actually get a track here as values[2&3] can be null
3700 VikTrack *track = NULL;
3701 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3702 gboolean xfer_all = FALSE;
3704 if ( values[MA_SUBTYPE] ) {
3706 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3707 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3710 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3711 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3714 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3717 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3721 else if ( !values[MA_CONFIRM] )
3722 xfer_all = TRUE; // i.e. whole layer
3724 if (track && !track->visible) {
3725 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3729 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3730 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3731 GTK_DIALOG_DESTROY_WITH_PARENT,
3733 GTK_RESPONSE_ACCEPT,
3735 GTK_RESPONSE_REJECT,
3738 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3739 GtkWidget *response_w = NULL;
3740 #if GTK_CHECK_VERSION (2, 20, 0)
3741 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3745 gtk_widget_grab_focus ( response_w );
3747 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3749 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3750 datasource_gps_clean_up ( dgs );
3751 gtk_widget_destroy ( dialog );
3755 // Get info from reused datasource dialog widgets
3756 gchar* protocol = datasource_gps_get_protocol ( dgs );
3757 gchar* port = datasource_gps_get_descriptor ( dgs );
3758 // NB don't free the above strings as they're references to values held elsewhere
3759 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3760 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3761 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3762 gboolean turn_off = datasource_gps_get_off ( dgs );
3764 gtk_widget_destroy ( dialog );
3766 // When called from the viewport - work the corresponding layerspanel:
3768 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3771 // Apply settings to transfer to the GPS device
3778 vik_layers_panel_get_viewport (vlp),
3787 * Acquire into this TRW Layer from any GPS Babel supported file
3789 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3791 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3792 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3793 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3794 VikViewport *vvp = vik_window_viewport(vw);
3796 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3799 static void trw_layer_new_wp ( menu_array_layer values )
3801 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3802 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3803 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3804 instead return true if you want to update. */
3805 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 ) {
3806 trw_layer_calculate_bounds_waypoints ( vtl );
3807 vik_layers_panel_emit_update ( vlp );
3811 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3813 vtl->current_track = vik_track_new();
3814 vik_track_set_defaults ( vtl->current_track );
3815 vtl->current_track->visible = TRUE;
3816 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3817 // Create track with the preferred colour from the layer properties
3818 vtl->current_track->color = vtl->track_color;
3820 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3821 vtl->current_track->has_color = TRUE;
3822 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3825 static void trw_layer_new_track ( menu_array_layer values )
3827 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3829 if ( ! vtl->current_track ) {
3830 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3831 new_track_create_common ( vtl, name );
3834 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3838 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3840 vtl->current_track = vik_track_new();
3841 vik_track_set_defaults ( vtl->current_track );
3842 vtl->current_track->visible = TRUE;
3843 vtl->current_track->is_route = TRUE;
3844 // By default make all routes red
3845 vtl->current_track->has_color = TRUE;
3846 gdk_color_parse ( "red", &vtl->current_track->color );
3847 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3850 static void trw_layer_new_route ( menu_array_layer values )
3852 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3854 if ( ! vtl->current_track ) {
3855 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3856 new_route_create_common ( vtl, name );
3858 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3862 static void trw_layer_auto_routes_view ( menu_array_layer values )
3864 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3865 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3867 if ( g_hash_table_size (vtl->routes) > 0 ) {
3868 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3869 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3870 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3871 vik_layers_panel_emit_update ( vlp );
3876 static void trw_layer_finish_track ( menu_array_layer values )
3878 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3879 vtl->current_track = NULL;
3880 vik_layer_emit_update ( VIK_LAYER(vtl) );
3883 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3885 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3886 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3888 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3889 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3890 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3891 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3892 vik_layers_panel_emit_update ( vlp );
3896 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3898 /* NB do not care if wp is visible or not */
3899 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3902 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3904 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3905 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3907 /* Only 1 waypoint - jump straight to it */
3908 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3909 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3910 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3912 /* If at least 2 waypoints - find center and then zoom to fit */
3913 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3915 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3916 maxmin[0].lat = vtl->waypoints_bbox.north;
3917 maxmin[1].lat = vtl->waypoints_bbox.south;
3918 maxmin[0].lon = vtl->waypoints_bbox.east;
3919 maxmin[1].lon = vtl->waypoints_bbox.west;
3920 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3923 vik_layers_panel_emit_update ( vlp );
3926 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3928 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3931 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3933 if ( values[MA_MISC] ) {
3934 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3935 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3939 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3941 static menu_array_layer pass_along;
3943 GtkWidget *export_submenu;
3944 pass_along[MA_VTL] = vtl;
3945 pass_along[MA_VLP] = vlp;
3947 item = gtk_menu_item_new();
3948 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3949 gtk_widget_show ( item );
3951 if ( vtl->current_track ) {
3952 if ( vtl->current_track->is_route )
3953 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3955 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3957 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3958 gtk_widget_show ( item );
3961 item = gtk_menu_item_new ();
3962 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3963 gtk_widget_show ( item );
3966 /* Now with icons */
3967 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3968 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3970 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3971 gtk_widget_show ( item );
3973 GtkWidget *view_submenu = gtk_menu_new();
3974 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3976 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3977 gtk_widget_show ( item );
3978 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3980 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3981 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3982 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3983 gtk_widget_show ( item );
3985 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3988 gtk_widget_show ( item );
3990 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3992 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3993 gtk_widget_show ( item );
3995 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3996 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3998 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3999 gtk_widget_show ( item );
4001 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4003 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4004 gtk_widget_show ( item );
4006 export_submenu = gtk_menu_new ();
4007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4010 gtk_widget_show ( item );
4011 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4013 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4015 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4016 gtk_widget_show ( item );
4018 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4020 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4021 gtk_widget_show ( item );
4023 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4025 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4026 gtk_widget_show ( item );
4028 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4030 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4031 gtk_widget_show ( item );
4033 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4035 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4036 gtk_widget_show ( item );
4038 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4039 item = gtk_menu_item_new_with_mnemonic ( external1 );
4040 g_free ( external1 );
4041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4042 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4043 gtk_widget_show ( item );
4045 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4046 item = gtk_menu_item_new_with_mnemonic ( external2 );
4047 g_free ( external2 );
4048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4049 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4050 gtk_widget_show ( item );
4052 GtkWidget *new_submenu = gtk_menu_new();
4053 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4054 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4055 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4056 gtk_widget_show(item);
4057 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4059 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4062 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4063 gtk_widget_show ( item );
4065 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4068 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4069 gtk_widget_show ( item );
4070 // Make it available only when a new track *not* already in progress
4071 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4073 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4074 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4076 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4077 gtk_widget_show ( item );
4078 // Make it available only when a new track *not* already in progress
4079 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4081 #ifdef VIK_CONFIG_GEOTAG
4082 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4084 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4085 gtk_widget_show ( item );
4088 GtkWidget *acquire_submenu = gtk_menu_new ();
4089 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4090 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4092 gtk_widget_show ( item );
4093 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4095 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4096 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4097 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4098 gtk_widget_show ( item );
4100 /* FIXME: only add menu when at least a routing engine has support for Directions */
4101 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4102 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4103 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4104 gtk_widget_show ( item );
4106 #ifdef VIK_CONFIG_OPENSTREETMAP
4107 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4109 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4110 gtk_widget_show ( item );
4112 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4114 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4115 gtk_widget_show ( item );
4118 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4120 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4121 gtk_widget_show ( item );
4123 #ifdef VIK_CONFIG_GEONAMES
4124 GtkWidget *wikipedia_submenu = gtk_menu_new();
4125 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4126 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4127 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4128 gtk_widget_show(item);
4129 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4131 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4134 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4135 gtk_widget_show ( item );
4137 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4138 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4139 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4140 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4141 gtk_widget_show ( item );
4144 #ifdef VIK_CONFIG_GEOCACHES
4145 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4147 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4148 gtk_widget_show ( item );
4151 #ifdef VIK_CONFIG_GEOTAG
4152 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4155 gtk_widget_show ( item );
4158 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4160 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4161 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4162 gtk_widget_show ( item );
4164 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4166 GtkWidget *upload_submenu = gtk_menu_new ();
4167 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4168 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4169 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4170 gtk_widget_show ( item );
4171 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4173 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4174 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4176 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4177 gtk_widget_show ( item );
4179 #ifdef VIK_CONFIG_OPENSTREETMAP
4180 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4183 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4184 gtk_widget_show ( item );
4187 GtkWidget *delete_submenu = gtk_menu_new ();
4188 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4189 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4190 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4191 gtk_widget_show ( item );
4192 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4194 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4197 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4198 gtk_widget_show ( item );
4200 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4201 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4203 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4204 gtk_widget_show ( item );
4206 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4209 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4210 gtk_widget_show ( item );
4212 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4215 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4216 gtk_widget_show ( item );
4218 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
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_waypoints), 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 Waypoints 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_waypoints_from_selection), pass_along );
4227 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4228 gtk_widget_show ( item );
4230 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4231 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4233 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4234 gtk_widget_show ( item );
4237 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4238 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4240 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4241 gtk_widget_show ( item );
4244 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4245 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4246 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4247 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4248 gtk_widget_show ( item );
4249 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4251 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4255 gtk_widget_show ( item );
4256 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4259 // Fake Waypoint UUIDs vi simple increasing integer
4260 static guint wp_uuid = 0;
4262 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4266 vik_waypoint_set_name (wp, name);
4268 if ( VIK_LAYER(vtl)->realized )
4270 // Do we need to create the sublayer:
4271 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4272 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4275 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4277 // Visibility column always needed for waypoints
4278 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 );
4280 // Actual setting of visibility dependent on the waypoint
4281 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4283 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4285 // Sort now as post_read is not called on a realized waypoint
4286 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4289 highest_wp_number_add_wp(vtl, name);
4290 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4294 // Fake Track UUIDs vi simple increasing integer
4295 static guint tr_uuid = 0;
4297 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4301 vik_track_set_name (t, name);
4303 if ( VIK_LAYER(vtl)->realized )
4305 // Do we need to create the sublayer:
4306 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4307 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4310 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4311 // Visibility column always needed for tracks
4312 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 );
4314 // Actual setting of visibility dependent on the track
4315 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4317 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4319 // Sort now as post_read is not called on a realized track
4320 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4323 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4325 trw_layer_update_treeview ( vtl, t );
4328 // Fake Route UUIDs vi simple increasing integer
4329 static guint rt_uuid = 0;
4331 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4335 vik_track_set_name (t, name);
4337 if ( VIK_LAYER(vtl)->realized )
4339 // Do we need to create the sublayer:
4340 if ( g_hash_table_size (vtl->routes) == 0 ) {
4341 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4344 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4345 // Visibility column always needed for routes
4346 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 );
4347 // Actual setting of visibility dependent on the route
4348 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4350 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4352 // Sort now as post_read is not called on a realized route
4353 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4356 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4358 trw_layer_update_treeview ( vtl, t );
4361 /* to be called whenever a track has been deleted or may have been changed. */
4362 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4364 if (vtl->current_tp_track == trk )
4365 trw_layer_cancel_current_tp ( vtl, FALSE );
4369 * Normally this is done to due the waypoint size preference having changed
4371 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4373 GHashTableIter iter;
4374 gpointer key, value;
4377 g_hash_table_iter_init ( &iter, vtl->waypoints );
4378 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4379 VikWaypoint *wp = VIK_WAYPOINT(value);
4381 // Reapply symbol setting to update the pixbuf
4382 gchar *tmp_symbol = g_strdup ( wp->symbol );
4383 vik_waypoint_set_symbol ( wp, tmp_symbol );
4384 g_free ( tmp_symbol );
4390 * trw_layer_new_unique_sublayer_name:
4392 * Allocates a unique new name
4394 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4397 gchar *newname = g_strdup(name);
4402 switch ( sublayer_type ) {
4403 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4404 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4406 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4407 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4410 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4413 // If found a name already in use try adding 1 to it and we try again
4415 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4417 newname = new_newname;
4420 } while ( id != NULL);
4425 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4427 // No more uniqueness of name forced when loading from a file
4428 // This now makes this function a little redunant as we just flow the parameters through
4429 vik_trw_layer_add_waypoint ( vtl, name, wp );
4432 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4434 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4435 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4436 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4437 vik_track_free ( tr );
4438 vtl->route_finder_append = FALSE; /* this means we have added it */
4441 // No more uniqueness of name forced when loading from a file
4443 vik_trw_layer_add_route ( vtl, name, tr );
4445 vik_trw_layer_add_track ( vtl, name, tr );
4447 if ( vtl->route_finder_check_added_track ) {
4448 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4449 vtl->route_finder_added_track = tr;
4454 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4456 *l = g_list_append(*l, id);
4460 * Move an item from one TRW layer to another TRW layer
4462 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4464 // TODO reconsider strategy when moving within layer (if anything...)
4465 gboolean rename = ( vtl_src != vtl_dest );
4469 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4470 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4474 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4476 newname = g_strdup ( trk->name );
4478 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4479 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4481 vik_trw_layer_delete_track ( vtl_src, trk );
4484 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4485 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4489 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4491 newname = g_strdup ( trk->name );
4493 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4494 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4496 vik_trw_layer_delete_route ( vtl_src, trk );
4499 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4500 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4504 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4506 newname = g_strdup ( wp->name );
4508 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4509 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4511 trw_layer_delete_waypoint ( vtl_src, wp );
4513 // Recalculate bounds even if not renamed as maybe dragged between layers
4514 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4515 trw_layer_calculate_bounds_waypoints ( vtl_src );
4519 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4521 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4522 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4524 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4525 GList *items = NULL;
4528 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4529 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4531 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4532 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4534 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4535 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4540 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4541 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4542 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4543 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4545 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4552 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4553 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4557 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4559 trku_udata *user_data = udata;
4560 if ( trk == user_data->trk ) {
4561 user_data->uuid = id;
4567 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4569 gboolean was_visible = FALSE;
4570 if ( trk && trk->name ) {
4572 if ( trk == vtl->current_track ) {
4573 vtl->current_track = NULL;
4574 vtl->current_tp_track = NULL;
4575 vtl->current_tp_id = NULL;
4576 vtl->moving_tp = FALSE;
4579 was_visible = trk->visible;
4581 if ( trk == vtl->route_finder_current_track )
4582 vtl->route_finder_current_track = NULL;
4584 if ( trk == vtl->route_finder_added_track )
4585 vtl->route_finder_added_track = NULL;
4591 // Hmmm, want key of it
4592 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4594 if ( trkf && udata.uuid ) {
4595 /* could be current_tp, so we have to check */
4596 trw_layer_cancel_tps_of_track ( vtl, trk );
4598 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4601 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4602 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4603 g_hash_table_remove ( vtl->tracks, udata.uuid );
4605 // If last sublayer, then remove sublayer container
4606 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4607 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4610 // Incase it was selected (no item delete signal ATM)
4611 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4617 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4619 gboolean was_visible = FALSE;
4621 if ( trk && trk->name ) {
4623 if ( trk == vtl->current_track ) {
4624 vtl->current_track = NULL;
4625 vtl->current_tp_track = NULL;
4626 vtl->current_tp_id = NULL;
4627 vtl->moving_tp = FALSE;
4630 was_visible = trk->visible;
4632 if ( trk == vtl->route_finder_current_track )
4633 vtl->route_finder_current_track = NULL;
4635 if ( trk == vtl->route_finder_added_track )
4636 vtl->route_finder_added_track = NULL;
4642 // Hmmm, want key of it
4643 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4645 if ( trkf && udata.uuid ) {
4646 /* could be current_tp, so we have to check */
4647 trw_layer_cancel_tps_of_track ( vtl, trk );
4649 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4652 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4653 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4654 g_hash_table_remove ( vtl->routes, udata.uuid );
4656 // If last sublayer, then remove sublayer container
4657 if ( g_hash_table_size (vtl->routes) == 0 ) {
4658 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4661 // Incase it was selected (no item delete signal ATM)
4662 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4668 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4670 gboolean was_visible = FALSE;
4672 if ( wp && wp->name ) {
4674 if ( wp == vtl->current_wp ) {
4675 vtl->current_wp = NULL;
4676 vtl->current_wp_id = NULL;
4677 vtl->moving_wp = FALSE;
4680 was_visible = wp->visible;
4686 // Hmmm, want key of it
4687 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4689 if ( wpf && udata.uuid ) {
4690 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4693 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4694 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4696 highest_wp_number_remove_wp(vtl, wp->name);
4697 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4699 // If last sublayer, then remove sublayer container
4700 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4701 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4704 // Incase it was selected (no item delete signal ATM)
4705 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4713 // Only for temporary use by trw_layer_delete_waypoint_by_name
4714 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4716 wpu_udata *user_data = udata;
4717 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4718 user_data->uuid = id;
4725 * Delete a waypoint by the given name
4726 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4727 * as there be multiple waypoints with the same name
4729 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4732 // Fake a waypoint with the given name
4733 udata.wp = vik_waypoint_new ();
4734 vik_waypoint_set_name (udata.wp, name);
4735 // Currently only the name is used in this waypoint find function
4738 // Hmmm, want key of it
4739 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4741 vik_waypoint_free (udata.wp);
4743 if ( wpf && udata.uuid )
4744 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4750 VikTrack *trk; // input
4751 gpointer uuid; // output
4754 // Only for temporary use by trw_layer_delete_track_by_name
4755 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4757 tpu_udata *user_data = udata;
4758 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4759 user_data->uuid = id;
4766 * Delete a track by the given name
4767 * NOTE: ATM this will delete the first encountered Track with the specified name
4768 * as there may be multiple tracks with the same name within the specified hash table
4770 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4773 // Fake a track with the given name
4774 udata.trk = vik_track_new ();
4775 vik_track_set_name (udata.trk, name);
4776 // Currently only the name is used in this waypoint find function
4779 // Hmmm, want key of it
4780 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4782 vik_track_free (udata.trk);
4784 if ( trkf && udata.uuid ) {
4785 // This could be a little better written...
4786 if ( vtl->tracks == ht_tracks )
4787 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4788 if ( vtl->routes == ht_tracks )
4789 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4796 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4798 vik_treeview_item_delete (vt, it );
4801 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4804 vtl->current_track = NULL;
4805 vtl->route_finder_current_track = NULL;
4806 vtl->route_finder_added_track = NULL;
4807 if (vtl->current_tp_track)
4808 trw_layer_cancel_current_tp(vtl, FALSE);
4810 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4811 g_hash_table_remove_all(vtl->routes_iters);
4812 g_hash_table_remove_all(vtl->routes);
4814 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4816 vik_layer_emit_update ( VIK_LAYER(vtl) );
4819 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4822 vtl->current_track = NULL;
4823 vtl->route_finder_current_track = NULL;
4824 vtl->route_finder_added_track = NULL;
4825 if (vtl->current_tp_track)
4826 trw_layer_cancel_current_tp(vtl, FALSE);
4828 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4829 g_hash_table_remove_all(vtl->tracks_iters);
4830 g_hash_table_remove_all(vtl->tracks);
4832 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4834 vik_layer_emit_update ( VIK_LAYER(vtl) );
4837 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4839 vtl->current_wp = NULL;
4840 vtl->current_wp_id = NULL;
4841 vtl->moving_wp = FALSE;
4843 highest_wp_number_reset(vtl);
4845 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4846 g_hash_table_remove_all(vtl->waypoints_iters);
4847 g_hash_table_remove_all(vtl->waypoints);
4849 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4851 vik_layer_emit_update ( VIK_LAYER(vtl) );
4854 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4856 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4857 // Get confirmation from the user
4858 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4859 _("Are you sure you want to delete all tracks in %s?"),
4860 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4861 vik_trw_layer_delete_all_tracks (vtl);
4864 static void trw_layer_delete_all_routes ( menu_array_layer values )
4866 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4867 // Get confirmation from the user
4868 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4869 _("Are you sure you want to delete all routes in %s?"),
4870 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4871 vik_trw_layer_delete_all_routes (vtl);
4874 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4876 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4877 // Get confirmation from the user
4878 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4879 _("Are you sure you want to delete all waypoints in %s?"),
4880 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4881 vik_trw_layer_delete_all_waypoints (vtl);
4884 static void trw_layer_delete_item ( menu_array_sublayer values )
4886 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4887 gboolean was_visible = FALSE;
4888 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4890 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4891 if ( wp && wp->name ) {
4892 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4893 // Get confirmation from the user
4894 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4895 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4896 _("Are you sure you want to delete the waypoint \"%s\"?"),
4899 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4902 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4904 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4905 if ( trk && trk->name ) {
4906 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4907 // Get confirmation from the user
4908 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4909 _("Are you sure you want to delete the track \"%s\"?"),
4912 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4917 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4918 if ( trk && trk->name ) {
4919 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4920 // Get confirmation from the user
4921 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4922 _("Are you sure you want to delete the route \"%s\"?"),
4925 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4929 vik_layer_emit_update ( VIK_LAYER(vtl) );
4933 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4935 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4937 vik_waypoint_set_name ( wp, new_name );
4939 // Now update the treeview as well
4944 // Need key of it for treeview update
4945 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4947 if ( wpf && udataU.uuid ) {
4948 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4951 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4952 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4958 * Maintain icon of waypoint in the treeview
4960 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4962 // update the treeview
4967 // Need key of it for treeview update
4968 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4970 if ( wpf && udataU.uuid ) {
4971 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4974 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4979 static void trw_layer_properties_item ( menu_array_sublayer values )
4981 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4982 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4984 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4986 if ( wp && wp->name )
4988 gboolean updated = FALSE;
4989 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4991 trw_layer_waypoint_rename ( vtl, wp, new_name );
4993 if ( updated && values[MA_TV_ITER] )
4994 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4996 if ( updated && VIK_LAYER(vtl)->visible )
4997 vik_layer_emit_update ( VIK_LAYER(vtl) );
5003 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5004 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5006 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5008 if ( tr && tr->name )
5010 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5021 * trw_layer_track_statistics:
5023 * Show track statistics.
5024 * ATM jump to the stats page in the properties
5025 * TODO: consider separating the stats into an individual dialog?
5027 static void trw_layer_track_statistics ( menu_array_sublayer values )
5029 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5031 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5032 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5034 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5036 if ( trk && trk->name ) {
5037 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5047 * Update the treeview of the track id - primarily to update the icon
5049 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5055 gpointer *trkf = NULL;
5056 if ( trk->is_route )
5057 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5059 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5061 if ( trkf && udata.uuid ) {
5063 GtkTreeIter *iter = NULL;
5064 if ( trk->is_route )
5065 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5067 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5070 // TODO: Make this a function
5071 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5072 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5073 ((trk->color.green & 0xff00) << 8) |
5074 (trk->color.blue & 0xff00);
5075 gdk_pixbuf_fill ( pixbuf, pixel );
5076 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5077 g_object_unref (pixbuf);
5084 Parameter 1 -> VikLayersPanel
5085 Parameter 2 -> VikLayer
5086 Parameter 3 -> VikViewport
5088 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5091 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5092 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5095 /* since vlp not set, vl & vvp should be valid instead! */
5097 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5098 vik_layer_emit_update ( VIK_LAYER(vl) );
5103 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5105 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5107 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5108 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5110 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5112 if ( track && track->trackpoints )
5113 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5116 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5118 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5120 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5121 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5123 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5125 if ( track && track->trackpoints )
5127 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5129 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5130 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5131 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5132 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5133 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5137 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5139 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5141 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5142 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5144 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5149 // Converting a track to a route can be a bit more complicated,
5150 // so give a chance to change our minds:
5151 if ( !trk->is_route &&
5152 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5153 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5155 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5156 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5161 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5164 trk_copy->is_route = !trk_copy->is_route;
5166 // ATM can't set name to self - so must create temporary copy
5167 gchar *name = g_strdup ( trk_copy->name );
5169 // Delete old one and then add new one
5170 if ( trk->is_route ) {
5171 vik_trw_layer_delete_route ( vtl, trk );
5172 vik_trw_layer_add_track ( vtl, name, trk_copy );
5175 // Extra route conversion bits...
5176 vik_track_merge_segments ( trk_copy );
5177 vik_track_to_routepoints ( trk_copy );
5179 vik_trw_layer_delete_track ( vtl, trk );
5180 vik_trw_layer_add_route ( vtl, name, trk_copy );
5184 // Update in case color of track / route changes when moving between sublayers
5185 vik_layer_emit_update ( VIK_LAYER(vtl) );
5188 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5190 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5192 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5193 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5195 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5198 vik_track_anonymize_times ( track );
5201 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5203 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5205 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5206 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5208 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5213 vtl->current_track = track;
5214 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);
5216 if ( track->trackpoints )
5217 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5221 * extend a track using route finder
5223 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5225 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5226 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5229 if ( !track->trackpoints )
5232 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5233 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5234 vtl->route_finder_current_track = track;
5235 vtl->route_finder_started = TRUE;
5237 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5243 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5245 // If have a vlp then perform a basic test to see if any DEM info available...
5247 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5249 if ( !g_list_length(dems) ) {
5250 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5258 * apply_dem_data_common:
5260 * A common function for applying the DEM values and reporting the results.
5262 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5264 if ( !trw_layer_dem_test ( vtl, vlp ) )
5267 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5268 // Inform user how much was changed
5270 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5271 g_snprintf(str, 64, tmp_str, changed);
5272 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5275 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5277 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5279 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5280 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5282 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5285 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5288 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5290 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5292 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5293 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5295 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5298 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5304 * A common function for applying the elevation smoothing and reporting the results.
5306 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5308 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5309 // Inform user how much was changed
5311 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5312 g_snprintf(str, 64, tmp_str, changed);
5313 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5319 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5321 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5323 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5324 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5326 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5331 smooth_it ( vtl, track, FALSE );
5334 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5336 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5338 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5339 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5341 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5346 smooth_it ( vtl, track, TRUE );
5350 * Commonal helper function
5352 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5355 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5356 g_snprintf(str, 64, tmp_str, changed);
5357 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5360 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5362 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5363 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5365 if ( !trw_layer_dem_test ( vtl, vlp ) )
5369 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5371 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5373 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5377 GHashTableIter iter;
5378 gpointer key, value;
5380 g_hash_table_iter_init ( &iter, vtl->waypoints );
5381 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5382 VikWaypoint *wp = VIK_WAYPOINT(value);
5383 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5386 wp_changed_message ( vtl, changed );
5389 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5391 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5392 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5394 if ( !trw_layer_dem_test ( vtl, vlp ) )
5398 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5400 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5402 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5406 GHashTableIter iter;
5407 gpointer key, value;
5409 g_hash_table_iter_init ( &iter, vtl->waypoints );
5410 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5411 VikWaypoint *wp = VIK_WAYPOINT(value);
5412 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5415 wp_changed_message ( vtl, changed );
5418 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5420 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5422 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5423 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5425 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5429 if ( !track->trackpoints )
5431 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5434 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5436 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5438 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5439 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5441 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5446 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5449 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5452 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5454 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5456 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5457 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5459 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5464 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5467 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5470 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5472 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5474 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5475 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5477 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5482 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5485 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5489 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5491 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5493 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5495 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5496 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5498 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5500 if ( trk && trk->trackpoints )
5502 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5503 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5504 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5505 if ( values[MA_VLP] )
5506 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5508 vik_layer_emit_update ( VIK_LAYER(vtl) );
5513 * Refine the selected track/route with a routing engine.
5514 * The routing engine is selected by the user, when requestiong the job.
5516 static void trw_layer_route_refine ( menu_array_sublayer values )
5518 static gint last_engine = 0;
5519 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5522 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5523 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5525 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5527 if ( trk && trk->trackpoints )
5529 /* Check size of the route */
5530 int nb = vik_track_get_tp_count(trk);
5532 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5533 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5534 GTK_MESSAGE_WARNING,
5535 GTK_BUTTONS_OK_CANCEL,
5536 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5538 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5539 gtk_widget_destroy ( dialog );
5540 if (response != GTK_RESPONSE_OK )
5543 /* Select engine from dialog */
5544 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5545 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5546 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5548 GTK_RESPONSE_REJECT,
5550 GTK_RESPONSE_ACCEPT,
5552 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5553 gtk_widget_show_all(label);
5555 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5557 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5558 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5559 gtk_widget_show_all(combo);
5561 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5563 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5565 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5567 /* Dialog validated: retrieve selected engine and do the job */
5568 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5569 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5572 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5574 /* Force saving track */
5575 /* FIXME: remove or rename this hack */
5576 vtl->route_finder_check_added_track = TRUE;
5579 vik_routing_engine_refine (routing, vtl, trk);
5581 /* FIXME: remove or rename this hack */
5582 if ( vtl->route_finder_added_track )
5583 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5585 vtl->route_finder_added_track = NULL;
5586 vtl->route_finder_check_added_track = FALSE;
5588 vik_layer_emit_update ( VIK_LAYER(vtl) );
5590 /* Restore cursor */
5591 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5593 gtk_widget_destroy ( dialog );
5597 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5599 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5600 trw_layer_tpwin_init ( vtl );
5603 /*************************************
5604 * merge/split by time routines
5605 *************************************/
5607 /* called for each key in track hash table.
5608 * If the current track has the same time stamp type, add it to the result,
5609 * except the one pointed by "exclude".
5610 * set exclude to NULL if there is no exclude to check.
5611 * Note that the result is in reverse (for performance reasons).
5616 gboolean with_timestamps;
5618 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5620 twt_udata *user_data = udata;
5621 VikTrackpoint *p1, *p2;
5622 VikTrack *trk = VIK_TRACK(value);
5623 if (trk == user_data->exclude) {
5627 if (trk->trackpoints) {
5628 p1 = vik_track_get_tp_first(trk);
5629 p2 = vik_track_get_tp_last(trk);
5631 if ( user_data->with_timestamps ) {
5632 if (!p1->has_timestamp || !p2->has_timestamp) {
5637 // Don't add tracks with timestamps when getting non timestamp tracks
5638 if (p1->has_timestamp || p2->has_timestamp) {
5644 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5647 /* called for each key in track hash table. if original track user_data[1] is close enough
5648 * to the passed one, add it to list in user_data[0]
5650 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5653 VikTrackpoint *p1, *p2;
5654 VikTrack *trk = VIK_TRACK(value);
5656 GList **nearby_tracks = ((gpointer *)user_data)[0];
5659 * detect reasons for not merging, and return
5660 * if no reason is found not to merge, then do it.
5663 twt_udata *udata = user_data;
5664 // Exclude the original track from the compiled list
5665 if (trk == udata->exclude) {
5669 t1 = vik_track_get_tp_first(trk)->timestamp;
5670 t2 = vik_track_get_tp_last(trk)->timestamp;
5672 if (trk->trackpoints) {
5673 p1 = vik_track_get_tp_first(trk);
5674 p2 = vik_track_get_tp_last(trk);
5676 if (!p1->has_timestamp || !p2->has_timestamp) {
5677 //g_print("no timestamp\n");
5681 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5682 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5683 if (! (abs(t1 - p2->timestamp) < threshold ||
5685 abs(p1->timestamp - t2) < threshold)
5692 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5695 /* comparison function used to sort tracks; a and b are hash table keys */
5696 /* Not actively used - can be restored if needed
5697 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5699 GHashTable *tracks = user_data;
5702 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5703 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5705 if (t1 < t2) return -1;
5706 if (t1 > t2) return 1;
5711 /* comparison function used to sort trackpoints */
5712 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5714 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5716 if (t1 < t2) return -1;
5717 if (t1 > t2) return 1;
5722 * comparison function which can be used to sort tracks or waypoints by name
5724 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5726 const gchar* namea = (const gchar*) a;
5727 const gchar* nameb = (const gchar*) b;
5728 if ( namea == NULL || nameb == NULL)
5731 // Same sort method as used in the vik_treeview_*_alphabetize functions
5732 return strcmp ( namea, nameb );
5736 * Attempt to merge selected track with other tracks specified by the user
5737 * Tracks to merge with must be of the same 'type' as the selected track -
5738 * either all with timestamps, or all without timestamps
5740 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5742 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5743 GList *other_tracks = NULL;
5744 GHashTable *ght_tracks;
5745 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5746 ght_tracks = vtl->routes;
5748 ght_tracks = vtl->tracks;
5750 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5755 if ( !track->trackpoints )
5759 udata.result = &other_tracks;
5760 udata.exclude = track;
5761 // Allow merging with 'similar' time type time tracks
5762 // i.e. either those times, or those without
5763 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5765 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5766 other_tracks = g_list_reverse(other_tracks);
5768 if ( !other_tracks ) {
5769 if ( udata.with_timestamps )
5770 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5772 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5776 // Sort alphabetically for user presentation
5777 // Convert into list of names for usage with dialog function
5778 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5779 GList *other_tracks_names = NULL;
5780 GList *iter = g_list_first ( other_tracks );
5782 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5783 iter = g_list_next ( iter );
5786 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5788 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5792 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5793 g_list_free(other_tracks);
5794 g_list_free(other_tracks_names);
5799 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5800 VikTrack *merge_track;
5801 if ( track->is_route )
5802 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5804 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5807 vik_track_steal_and_append_trackpoints ( track, merge_track );
5808 if ( track->is_route )
5809 vik_trw_layer_delete_route (vtl, merge_track);
5811 vik_trw_layer_delete_track (vtl, merge_track);
5812 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5815 for (l = merge_list; l != NULL; l = g_list_next(l))
5817 g_list_free(merge_list);
5819 vik_layer_emit_update( VIK_LAYER(vtl) );
5823 // c.f. trw_layer_sorted_track_id_by_name_list
5824 // but don't add the specified track to the list (normally current track)
5825 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5827 twt_udata *user_data = udata;
5830 if (trk == user_data->exclude) {
5834 // Sort named list alphabetically
5835 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5839 * Join - this allows combining 'tracks' and 'track routes'
5840 * i.e. doesn't care about whether tracks have consistent timestamps
5841 * ATM can only append one track at a time to the currently selected track
5843 static void trw_layer_append_track ( menu_array_sublayer values )
5846 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5848 GHashTable *ght_tracks;
5849 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5850 ght_tracks = vtl->routes;
5852 ght_tracks = vtl->tracks;
5854 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5859 GList *other_tracks_names = NULL;
5861 // Sort alphabetically for user presentation
5862 // Convert into list of names for usage with dialog function
5863 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5865 udata.result = &other_tracks_names;
5866 udata.exclude = trk;
5868 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5870 // Note the limit to selecting one track only
5871 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5872 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5873 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5876 trk->is_route ? _("Append Route"): _("Append Track"),
5877 trk->is_route ? _("Select the route to append after the current route") :
5878 _("Select the track to append after the current track") );
5880 g_list_free(other_tracks_names);
5882 // It's a list, but shouldn't contain more than one other track!
5883 if ( append_list ) {
5885 for (l = append_list; l != NULL; l = g_list_next(l)) {
5886 // TODO: at present this uses the first track found by name,
5887 // which with potential multiple same named tracks may not be the one selected...
5888 VikTrack *append_track;
5889 if ( trk->is_route )
5890 append_track = vik_trw_layer_get_route ( vtl, l->data );
5892 append_track = vik_trw_layer_get_track ( vtl, l->data );
5894 if ( append_track ) {
5895 vik_track_steal_and_append_trackpoints ( trk, append_track );
5896 if ( trk->is_route )
5897 vik_trw_layer_delete_route (vtl, append_track);
5899 vik_trw_layer_delete_track (vtl, append_track);
5902 for (l = append_list; l != NULL; l = g_list_next(l))
5904 g_list_free(append_list);
5906 vik_layer_emit_update( VIK_LAYER(vtl) );
5911 * Very similar to trw_layer_append_track for joining
5912 * but this allows selection from the 'other' list
5913 * If a track is selected, then is shows routes and joins the selected one
5914 * If a route is selected, then is shows tracks and joins the selected one
5916 static void trw_layer_append_other ( menu_array_sublayer values )
5919 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5921 GHashTable *ght_mykind, *ght_others;
5922 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5923 ght_mykind = vtl->routes;
5924 ght_others = vtl->tracks;
5927 ght_mykind = vtl->tracks;
5928 ght_others = vtl->routes;
5931 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5936 GList *other_tracks_names = NULL;
5938 // Sort alphabetically for user presentation
5939 // Convert into list of names for usage with dialog function
5940 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5942 udata.result = &other_tracks_names;
5943 udata.exclude = trk;
5945 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5947 // Note the limit to selecting one track only
5948 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5949 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5950 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5953 trk->is_route ? _("Append Track"): _("Append Route"),
5954 trk->is_route ? _("Select the track to append after the current route") :
5955 _("Select the route to append after the current track") );
5957 g_list_free(other_tracks_names);
5959 // It's a list, but shouldn't contain more than one other track!
5960 if ( append_list ) {
5962 for (l = append_list; l != NULL; l = g_list_next(l)) {
5963 // TODO: at present this uses the first track found by name,
5964 // which with potential multiple same named tracks may not be the one selected...
5966 // Get FROM THE OTHER TYPE list
5967 VikTrack *append_track;
5968 if ( trk->is_route )
5969 append_track = vik_trw_layer_get_track ( vtl, l->data );
5971 append_track = vik_trw_layer_get_route ( vtl, l->data );
5973 if ( append_track ) {
5975 if ( !append_track->is_route &&
5976 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5977 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5979 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5980 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5981 vik_track_merge_segments ( append_track );
5982 vik_track_to_routepoints ( append_track );
5989 vik_track_steal_and_append_trackpoints ( trk, append_track );
5991 // Delete copied which is FROM THE OTHER TYPE list
5992 if ( trk->is_route )
5993 vik_trw_layer_delete_track (vtl, append_track);
5995 vik_trw_layer_delete_route (vtl, append_track);
5998 for (l = append_list; l != NULL; l = g_list_next(l))
6000 g_list_free(append_list);
6001 vik_layer_emit_update( VIK_LAYER(vtl) );
6005 /* merge by segments */
6006 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6008 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6009 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6010 guint segments = vik_track_merge_segments ( trk );
6011 // NB currently no need to redraw as segments not actually shown on the display
6012 // However inform the user of what happened:
6014 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6015 g_snprintf(str, 64, tmp_str, segments);
6016 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6019 /* merge by time routine */
6020 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6022 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6026 GList *tracks_with_timestamp = NULL;
6027 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6028 if (orig_trk->trackpoints &&
6029 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6030 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6035 udata.result = &tracks_with_timestamp;
6036 udata.exclude = orig_trk;
6037 udata.with_timestamps = TRUE;
6038 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6039 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6041 if (!tracks_with_timestamp) {
6042 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6045 g_list_free(tracks_with_timestamp);
6047 static guint threshold_in_minutes = 1;
6048 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6049 _("Merge Threshold..."),
6050 _("Merge when time between tracks less than:"),
6051 &threshold_in_minutes)) {
6055 // keep attempting to merge all tracks until no merges within the time specified is possible
6056 gboolean attempt_merge = TRUE;
6057 GList *nearby_tracks = NULL;
6059 static gpointer params[3];
6061 while ( attempt_merge ) {
6063 // Don't try again unless tracks have changed
6064 attempt_merge = FALSE;
6066 trps = orig_trk->trackpoints;
6070 if (nearby_tracks) {
6071 g_list_free(nearby_tracks);
6072 nearby_tracks = NULL;
6075 params[0] = &nearby_tracks;
6076 params[1] = (gpointer)trps;
6077 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6079 /* get a list of adjacent-in-time tracks */
6080 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6083 GList *l = nearby_tracks;
6085 /* remove trackpoints from merged track, delete track */
6086 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6087 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6089 // Tracks have changed, therefore retry again against all the remaining tracks
6090 attempt_merge = TRUE;
6095 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6098 g_list_free(nearby_tracks);
6100 vik_layer_emit_update( VIK_LAYER(vtl) );
6104 * Split a track at the currently selected trackpoint
6106 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6108 if ( !vtl->current_tpl )
6111 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6112 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6114 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6115 GList *newglist = g_list_alloc ();
6116 newglist->prev = NULL;
6117 newglist->next = vtl->current_tpl->next;
6118 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6119 tr->trackpoints = newglist;
6121 vtl->current_tpl->next->prev = newglist; /* end old track here */
6122 vtl->current_tpl->next = NULL;
6124 // Bounds of the selected track changed due to the split
6125 vik_track_calculate_bounds ( vtl->current_tp_track );
6127 vtl->current_tpl = newglist; /* change tp to first of new track. */
6128 vtl->current_tp_track = tr;
6131 vik_trw_layer_add_route ( vtl, name, tr );
6133 vik_trw_layer_add_track ( vtl, name, tr );
6135 // Bounds of the new track created by the split
6136 vik_track_calculate_bounds ( tr );
6142 // Also need id of newly created track
6145 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6147 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6149 if ( trkf && udata.uuid )
6150 vtl->current_tp_id = udata.uuid;
6152 vtl->current_tp_id = NULL;
6154 vik_layer_emit_update(VIK_LAYER(vtl));
6160 /* split by time routine */
6161 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6163 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6164 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6165 GList *trps = track->trackpoints;
6167 GList *newlists = NULL;
6168 GList *newtps = NULL;
6169 static guint thr = 1;
6176 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6177 _("Split Threshold..."),
6178 _("Split when time between trackpoints exceeds:"),
6183 /* iterate through trackpoints, and copy them into new lists without touching original list */
6184 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6188 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6190 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6193 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6194 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6195 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6197 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6202 if (ts - prev_ts > thr*60) {
6203 /* flush accumulated trackpoints into new list */
6204 newlists = g_list_append(newlists, g_list_reverse(newtps));
6208 /* accumulate trackpoint copies in newtps, in reverse order */
6209 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6211 iter = g_list_next(iter);
6214 newlists = g_list_append(newlists, g_list_reverse(newtps));
6217 /* put lists of trackpoints into tracks */
6219 // Only bother updating if the split results in new tracks
6220 if (g_list_length (newlists) > 1) {
6225 tr = vik_track_copy ( track, FALSE );
6226 tr->trackpoints = (GList *)(iter->data);
6228 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6229 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6230 g_free ( new_tr_name );
6231 vik_track_calculate_bounds ( tr );
6232 iter = g_list_next(iter);
6234 // Remove original track and then update the display
6235 vik_trw_layer_delete_track (vtl, track);
6236 vik_layer_emit_update(VIK_LAYER(vtl));
6238 g_list_free(newlists);
6242 * Split a track by the number of points as specified by the user
6244 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6246 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6248 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6249 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6251 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6256 // Check valid track
6257 GList *trps = track->trackpoints;
6261 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6262 _("Split Every Nth Point"),
6263 _("Split on every Nth point:"),
6264 250, // Default value as per typical limited track capacity of various GPS devices
6268 // Was a valid number returned?
6274 GList *newlists = NULL;
6275 GList *newtps = NULL;
6280 /* accumulate trackpoint copies in newtps, in reverse order */
6281 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6283 if (count >= points) {
6284 /* flush accumulated trackpoints into new list */
6285 newlists = g_list_append(newlists, g_list_reverse(newtps));
6289 iter = g_list_next(iter);
6292 // If there is a remaining chunk put that into the new split list
6293 // This may well be the whole track if no split points were encountered
6295 newlists = g_list_append(newlists, g_list_reverse(newtps));
6298 /* put lists of trackpoints into tracks */
6300 // Only bother updating if the split results in new tracks
6301 if (g_list_length (newlists) > 1) {
6306 tr = vik_track_copy ( track, FALSE );
6307 tr->trackpoints = (GList *)(iter->data);
6309 if ( track->is_route ) {
6310 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6311 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6314 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6315 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6317 g_free ( new_tr_name );
6318 vik_track_calculate_bounds ( tr );
6320 iter = g_list_next(iter);
6322 // Remove original track and then update the display
6323 if ( track->is_route )
6324 vik_trw_layer_delete_route (vtl, track);
6326 vik_trw_layer_delete_track (vtl, track);
6327 vik_layer_emit_update(VIK_LAYER(vtl));
6329 g_list_free(newlists);
6333 * Split a track at the currently selected trackpoint
6335 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6337 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6338 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6339 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6343 * Split a track by its segments
6344 * Routes do not have segments so don't call this for routes
6346 static void trw_layer_split_segments ( menu_array_sublayer values )
6348 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6349 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6356 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6359 for ( i = 0; i < ntracks; i++ ) {
6361 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6362 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6363 g_free ( new_tr_name );
6368 // Remove original track
6369 vik_trw_layer_delete_track ( vtl, trk );
6370 vik_layer_emit_update ( VIK_LAYER(vtl) );
6373 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6376 /* end of split/merge routines */
6378 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6382 // Find available adjacent trackpoint
6383 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6384 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6385 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6387 // Delete current trackpoint
6388 vik_trackpoint_free ( vtl->current_tpl->data );
6389 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6391 // Set to current to the available adjacent trackpoint
6392 vtl->current_tpl = new_tpl;
6394 if ( vtl->current_tp_track ) {
6395 vik_track_calculate_bounds ( vtl->current_tp_track );
6399 // Delete current trackpoint
6400 vik_trackpoint_free ( vtl->current_tpl->data );
6401 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6402 trw_layer_cancel_current_tp ( vtl, FALSE );
6407 * Delete the selected point
6409 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6411 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6413 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6414 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6416 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6421 if ( !vtl->current_tpl )
6424 trw_layer_trackpoint_selected_delete ( vtl, trk );
6426 // Track has been updated so update tps:
6427 trw_layer_cancel_tps_of_track ( vtl, trk );
6429 vik_layer_emit_update ( VIK_LAYER(vtl) );
6433 * Delete adjacent track points at the same position
6434 * AKA Delete Dulplicates on the Properties Window
6436 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6438 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6440 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6441 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6443 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6448 gulong removed = vik_track_remove_dup_points ( trk );
6450 // Track has been updated so update tps:
6451 trw_layer_cancel_tps_of_track ( vtl, trk );
6453 // Inform user how much was deleted as it's not obvious from the normal view
6455 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6456 g_snprintf(str, 64, tmp_str, removed);
6457 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6459 vik_layer_emit_update ( VIK_LAYER(vtl) );
6463 * Delete adjacent track points with the same timestamp
6464 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6466 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6468 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6470 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6471 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6473 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6478 gulong removed = vik_track_remove_same_time_points ( trk );
6480 // Track has been updated so update tps:
6481 trw_layer_cancel_tps_of_track ( vtl, trk );
6483 // Inform user how much was deleted as it's not obvious from the normal view
6485 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6486 g_snprintf(str, 64, tmp_str, removed);
6487 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6489 vik_layer_emit_update ( VIK_LAYER(vtl) );
6495 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6497 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6499 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6500 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6502 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6507 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6509 vik_layer_emit_update ( VIK_LAYER(vtl) );
6512 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6514 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6516 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6517 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6519 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6524 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6526 vik_layer_emit_update ( VIK_LAYER(vtl) );
6532 static void trw_layer_reverse ( menu_array_sublayer values )
6534 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6536 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6537 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6539 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6544 vik_track_reverse ( track );
6546 vik_layer_emit_update ( VIK_LAYER(vtl) );
6550 * Open a diary at the specified date
6552 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6555 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6556 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6557 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6558 g_error_free ( err );
6564 * Open a diary at the date of the track or waypoint
6566 static void trw_layer_diary ( menu_array_sublayer values )
6568 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6570 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6571 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6577 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6578 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6579 trw_layer_diary_open ( vtl, date_buf );
6582 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6584 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6585 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6591 if ( wpt->has_timestamp ) {
6592 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6593 trw_layer_diary_open ( vtl, date_buf );
6596 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6601 * Similar to trw_layer_enum_item, but this uses a sorted method
6604 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6606 GList **list = (GList**)udata;
6607 // *list = g_list_prepend(*all, key); //unsorted method
6608 // Sort named list alphabetically
6609 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6614 * Now Waypoint specific sort
6616 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6618 GList **list = (GList**)udata;
6619 // Sort named list alphabetically
6620 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6624 * Track specific sort
6626 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6628 GList **list = (GList**)udata;
6629 // Sort named list alphabetically
6630 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6635 gboolean has_same_track_name;
6636 const gchar *same_track_name;
6637 } same_track_name_udata;
6639 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6641 const gchar* namea = (const gchar*) aa;
6642 const gchar* nameb = (const gchar*) bb;
6645 gint result = strcmp ( namea, nameb );
6647 if ( result == 0 ) {
6648 // Found two names the same
6649 same_track_name_udata *user_data = udata;
6650 user_data->has_same_track_name = TRUE;
6651 user_data->same_track_name = namea;
6654 // Leave ordering the same
6659 * Find out if any tracks have the same name in this hash table
6661 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6663 // Sort items by name, then compare if any next to each other are the same
6665 GList *track_names = NULL;
6666 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6669 if ( ! track_names )
6672 same_track_name_udata udata;
6673 udata.has_same_track_name = FALSE;
6675 // Use sort routine to traverse list comparing items
6676 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6677 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6678 // Still no tracks...
6682 return udata.has_same_track_name;
6686 * Force unqiue track names for the track table specified
6687 * Note the panel is a required parameter to enable the update of the names displayed
6688 * Specify if on tracks or else on routes
6690 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6692 // . Search list for an instance of repeated name
6693 // . get track of this name
6694 // . create new name
6695 // . rename track & update equiv. treeview iter
6696 // . repeat until all different
6698 same_track_name_udata udata;
6700 GList *track_names = NULL;
6701 udata.has_same_track_name = FALSE;
6702 udata.same_track_name = NULL;
6704 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6707 if ( ! track_names )
6710 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6712 // Still no tracks...
6713 if ( ! dummy_list1 )
6716 while ( udata.has_same_track_name ) {
6718 // Find a track with the same name
6721 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6723 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6727 g_critical("Houston, we've had a problem.");
6728 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6729 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6734 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6735 vik_track_set_name ( trk, newname );
6741 // Need want key of it for treeview update
6742 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6744 if ( trkf && udataU.uuid ) {
6748 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6750 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6753 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6755 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6757 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6761 // Start trying to find same names again...
6763 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6764 udata.has_same_track_name = FALSE;
6765 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6767 // No tracks any more - give up searching
6768 if ( ! dummy_list2 )
6769 udata.has_same_track_name = FALSE;
6773 vik_layers_panel_emit_update ( vlp );
6776 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6778 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6781 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6782 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6783 iter = &(vtl->tracks_iter);
6784 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6786 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6787 iter = &(vtl->routes_iter);
6788 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6790 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6791 iter = &(vtl->waypoints_iter);
6792 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6796 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6799 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6801 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6804 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6805 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6806 iter = &(vtl->tracks_iter);
6807 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6809 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6810 iter = &(vtl->routes_iter);
6811 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6813 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6814 iter = &(vtl->waypoints_iter);
6815 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6819 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6825 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6827 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6830 // Ensure list of track names offered is unique
6831 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6832 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6833 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6834 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6840 // Sort list alphabetically for better presentation
6841 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6844 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6848 // Get list of items to delete from the user
6849 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6852 _("Delete Selection"),
6853 _("Select tracks to delete"));
6856 // Delete requested tracks
6857 // since specificly requested, IMHO no need for extra confirmation
6858 if ( delete_list ) {
6860 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6861 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6862 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6864 g_list_free(delete_list);
6865 vik_layer_emit_update( VIK_LAYER(vtl) );
6872 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6874 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6877 // Ensure list of track names offered is unique
6878 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6879 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6880 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6881 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6887 // Sort list alphabetically for better presentation
6888 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6891 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6895 // Get list of items to delete from the user
6896 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6899 _("Delete Selection"),
6900 _("Select routes to delete") );
6903 // Delete requested routes
6904 // since specificly requested, IMHO no need for extra confirmation
6905 if ( delete_list ) {
6907 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6908 // This deletes first route it finds of that name (but uniqueness is enforced above)
6909 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6911 g_list_free(delete_list);
6912 vik_layer_emit_update( VIK_LAYER(vtl) );
6917 gboolean has_same_waypoint_name;
6918 const gchar *same_waypoint_name;
6919 } same_waypoint_name_udata;
6921 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6923 const gchar* namea = (const gchar*) aa;
6924 const gchar* nameb = (const gchar*) bb;
6927 gint result = strcmp ( namea, nameb );
6929 if ( result == 0 ) {
6930 // Found two names the same
6931 same_waypoint_name_udata *user_data = udata;
6932 user_data->has_same_waypoint_name = TRUE;
6933 user_data->same_waypoint_name = namea;
6936 // Leave ordering the same
6941 * Find out if any waypoints have the same name in this layer
6943 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6945 // Sort items by name, then compare if any next to each other are the same
6947 GList *waypoint_names = NULL;
6948 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6951 if ( ! waypoint_names )
6954 same_waypoint_name_udata udata;
6955 udata.has_same_waypoint_name = FALSE;
6957 // Use sort routine to traverse list comparing items
6958 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6959 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6960 // Still no waypoints...
6964 return udata.has_same_waypoint_name;
6968 * Force unqiue waypoint names for this layer
6969 * Note the panel is a required parameter to enable the update of the names displayed
6971 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6973 // . Search list for an instance of repeated name
6974 // . get waypoint of this name
6975 // . create new name
6976 // . rename waypoint & update equiv. treeview iter
6977 // . repeat until all different
6979 same_waypoint_name_udata udata;
6981 GList *waypoint_names = NULL;
6982 udata.has_same_waypoint_name = FALSE;
6983 udata.same_waypoint_name = NULL;
6985 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6988 if ( ! waypoint_names )
6991 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6993 // Still no waypoints...
6994 if ( ! dummy_list1 )
6997 while ( udata.has_same_waypoint_name ) {
6999 // Find a waypoint with the same name
7000 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7004 g_critical("Houston, we've had a problem.");
7005 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7006 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7011 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7013 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7015 // Start trying to find same names again...
7016 waypoint_names = NULL;
7017 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7018 udata.has_same_waypoint_name = FALSE;
7019 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7021 // No waypoints any more - give up searching
7022 if ( ! dummy_list2 )
7023 udata.has_same_waypoint_name = FALSE;
7027 vik_layers_panel_emit_update ( vlp );
7033 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7035 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7038 // Ensure list of waypoint names offered is unique
7039 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7040 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7041 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7042 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7048 // Sort list alphabetically for better presentation
7049 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7051 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7055 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7057 // Get list of items to delete from the user
7058 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7061 _("Delete Selection"),
7062 _("Select waypoints to delete"));
7065 // Delete requested waypoints
7066 // since specificly requested, IMHO no need for extra confirmation
7067 if ( delete_list ) {
7069 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7070 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7071 trw_layer_delete_waypoint_by_name (vtl, l->data);
7073 g_list_free(delete_list);
7075 trw_layer_calculate_bounds_waypoints ( vtl );
7076 vik_layer_emit_update( VIK_LAYER(vtl) );
7084 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7086 vik_treeview_item_toggle_visible ( vt, it );
7092 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7094 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7100 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7102 wp->visible = GPOINTER_TO_INT (on_off);
7108 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7110 wp->visible = !wp->visible;
7116 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7118 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7119 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7120 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7121 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7123 vik_layer_emit_update ( VIK_LAYER(vtl) );
7129 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7131 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7132 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7133 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7134 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7136 vik_layer_emit_update ( VIK_LAYER(vtl) );
7142 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7144 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7145 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7146 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7148 vik_layer_emit_update ( VIK_LAYER(vtl) );
7154 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7156 trk->visible = GPOINTER_TO_INT (on_off);
7162 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7164 trk->visible = !trk->visible;
7170 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7172 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7173 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7174 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7175 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7177 vik_layer_emit_update ( VIK_LAYER(vtl) );
7183 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7185 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7186 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7187 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7188 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7190 vik_layer_emit_update ( VIK_LAYER(vtl) );
7196 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7198 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7199 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7200 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7202 vik_layer_emit_update ( VIK_LAYER(vtl) );
7208 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7210 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7211 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7212 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7213 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7215 vik_layer_emit_update ( VIK_LAYER(vtl) );
7221 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7223 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7224 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7225 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7226 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7228 vik_layer_emit_update ( VIK_LAYER(vtl) );
7234 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7236 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7237 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7238 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7240 vik_layer_emit_update ( VIK_LAYER(vtl) );
7244 * vik_trw_layer_build_waypoint_list_t:
7246 * Helper function to construct a list of #vik_trw_waypoint_list_t
7248 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7250 GList *waypoints_and_layers = NULL;
7251 // build waypoints_and_layers list
7252 while ( waypoints ) {
7253 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7254 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7256 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7257 waypoints = g_list_next ( waypoints );
7259 return waypoints_and_layers;
7263 * trw_layer_create_waypoint_list:
7265 * Create the latest list of waypoints with the associated layer(s)
7266 * Although this will always be from a single layer here
7268 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7270 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7271 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7273 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7277 * trw_layer_analyse_close:
7279 * Stuff to do on dialog closure
7281 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7283 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7284 gtk_widget_destroy ( dialog );
7285 vtl->tracks_analysis_dialog = NULL;
7289 * vik_trw_layer_build_track_list_t:
7291 * Helper function to construct a list of #vik_trw_track_list_t
7293 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7295 GList *tracks_and_layers = NULL;
7296 // build tracks_and_layers list
7298 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7299 vtdl->trk = VIK_TRACK(tracks->data);
7301 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7302 tracks = g_list_next ( tracks );
7304 return tracks_and_layers;
7308 * trw_layer_create_track_list:
7310 * Create the latest list of tracks with the associated layer(s)
7311 * Although this will always be from a single layer here
7313 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7315 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7316 GList *tracks = NULL;
7317 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7318 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7320 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7322 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7325 static void trw_layer_tracks_stats ( menu_array_layer values )
7327 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7328 // There can only be one!
7329 if ( vtl->tracks_analysis_dialog )
7332 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7333 VIK_LAYER(vtl)->name,
7335 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7336 trw_layer_create_track_list,
7337 trw_layer_analyse_close );
7343 static void trw_layer_routes_stats ( menu_array_layer values )
7345 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7346 // There can only be one!
7347 if ( vtl->tracks_analysis_dialog )
7350 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7351 VIK_LAYER(vtl)->name,
7353 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7354 trw_layer_create_track_list,
7355 trw_layer_analyse_close );
7358 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7360 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7361 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7363 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7366 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7368 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7369 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7372 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7373 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7377 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7379 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7380 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7383 if ( !strncmp(wp->comment, "http", 4) ) {
7384 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7385 } else if ( !strncmp(wp->description, "http", 4) ) {
7386 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7390 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7392 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7394 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7396 // No actual change to the name supplied
7398 if (strcmp(newname, wp->name) == 0 )
7401 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7404 // An existing waypoint has been found with the requested name
7405 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7406 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7411 // Update WP name and refresh the treeview
7412 vik_waypoint_set_name (wp, newname);
7414 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7415 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7417 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7422 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7424 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7426 // No actual change to the name supplied
7428 if (strcmp(newname, trk->name) == 0)
7431 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7434 // An existing track has been found with the requested name
7435 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7436 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7440 // Update track name and refresh GUI parts
7441 vik_track_set_name (trk, newname);
7443 // Update any subwindows that could be displaying this track which has changed name
7444 // Only one Track Edit Window
7445 if ( l->current_tp_track == trk && l->tpwin ) {
7446 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7448 // Property Dialog of the track
7449 vik_trw_layer_propwin_update ( trk );
7451 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7452 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7454 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7459 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7461 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7463 // No actual change to the name supplied
7465 if (strcmp(newname, trk->name) == 0)
7468 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7471 // An existing track has been found with the requested name
7472 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7473 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7477 // Update track name and refresh GUI parts
7478 vik_track_set_name (trk, newname);
7480 // Update any subwindows that could be displaying this track which has changed name
7481 // Only one Track Edit Window
7482 if ( l->current_tp_track == trk && l->tpwin ) {
7483 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7485 // Property Dialog of the track
7486 vik_trw_layer_propwin_update ( trk );
7488 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7489 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7491 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7498 static gboolean is_valid_geocache_name ( gchar *str )
7500 gint len = strlen ( str );
7501 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]));
7504 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7506 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7507 a_acquire_set_filter_track ( trk );
7510 #ifdef VIK_CONFIG_GOOGLE
7511 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7513 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7514 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7517 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7519 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7521 gchar *escaped = uri_escape ( tr->comment );
7522 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7523 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7530 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7531 /* viewpoint is now available instead */
7532 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7534 static menu_array_sublayer pass_along;
7536 gboolean rv = FALSE;
7538 pass_along[MA_VTL] = l;
7539 pass_along[MA_VLP] = vlp;
7540 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7541 pass_along[MA_SUBLAYER_ID] = sublayer;
7542 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7543 pass_along[MA_VVP] = vvp;
7544 pass_along[MA_TV_ITER] = iter;
7545 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7547 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7551 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7552 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7553 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7554 gtk_widget_show ( item );
7556 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7557 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7558 if (tr && tr->property_dialog)
7559 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7561 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7562 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7563 if (tr && tr->property_dialog)
7564 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7567 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7568 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7569 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7570 gtk_widget_show ( item );
7572 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7574 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7575 gtk_widget_show ( item );
7577 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7578 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7579 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7580 gtk_widget_show ( item );
7582 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7584 // Always create separator as now there is always at least the transform menu option
7585 item = gtk_menu_item_new ();
7586 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7587 gtk_widget_show ( item );
7589 /* could be a right-click using the tool */
7590 if ( vlp != NULL ) {
7591 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7592 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7594 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7595 gtk_widget_show ( item );
7598 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7600 if ( wp && wp->name ) {
7601 if ( is_valid_geocache_name ( wp->name ) ) {
7602 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7604 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7605 gtk_widget_show ( item );
7607 #ifdef VIK_CONFIG_GEOTAG
7608 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7610 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7611 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7612 gtk_widget_show ( item );
7616 if ( wp && wp->image )
7618 // Set up image paramater
7619 pass_along[MA_MISC] = wp->image;
7621 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7622 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
7623 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7624 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7625 gtk_widget_show ( item );
7627 #ifdef VIK_CONFIG_GEOTAG
7628 GtkWidget *geotag_submenu = gtk_menu_new ();
7629 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7630 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7631 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7632 gtk_widget_show ( item );
7633 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7635 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7636 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7637 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7638 gtk_widget_show ( item );
7640 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7641 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7642 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7643 gtk_widget_show ( item );
7649 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7650 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7651 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7652 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7653 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7654 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7655 gtk_widget_show ( item );
7661 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7662 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7664 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7665 gtk_widget_show ( item );
7666 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7667 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7668 gtk_widget_set_sensitive ( item, TRUE );
7670 gtk_widget_set_sensitive ( item, FALSE );
7673 item = gtk_menu_item_new ();
7674 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7675 gtk_widget_show ( item );
7678 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7681 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7682 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7683 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7684 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7685 gtk_widget_show ( item );
7688 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7690 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7691 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7693 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7694 gtk_widget_show ( item );
7696 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7697 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7699 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7700 gtk_widget_show ( item );
7702 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7705 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7706 gtk_widget_show ( item );
7708 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7709 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7711 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7712 gtk_widget_show ( item );
7714 GtkWidget *vis_submenu = gtk_menu_new ();
7715 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7716 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7717 gtk_widget_show ( item );
7718 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7720 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7721 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7722 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7723 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7724 gtk_widget_show ( item );
7726 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7727 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7729 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7730 gtk_widget_show ( item );
7732 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7733 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7734 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7735 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7736 gtk_widget_show ( item );
7738 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7741 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7744 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7748 if ( l->current_track && !l->current_track->is_route ) {
7749 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7750 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7751 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7752 gtk_widget_show ( item );
7754 item = gtk_menu_item_new ();
7755 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7756 gtk_widget_show ( item );
7759 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7761 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7762 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7763 gtk_widget_show ( item );
7765 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7766 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7767 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7768 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7769 gtk_widget_show ( item );
7770 // Make it available only when a new track *not* already in progress
7771 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7773 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7776 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7777 gtk_widget_show ( item );
7779 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7780 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7782 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7783 gtk_widget_show ( item );
7785 GtkWidget *vis_submenu = gtk_menu_new ();
7786 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7787 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7788 gtk_widget_show ( item );
7789 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7791 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7794 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7795 gtk_widget_show ( item );
7797 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7798 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7801 gtk_widget_show ( item );
7803 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7804 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7805 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7806 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7808 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7812 gtk_widget_show ( item );
7814 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7815 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7816 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7817 gtk_widget_show ( item );
7820 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7824 if ( l->current_track && l->current_track->is_route ) {
7825 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7826 // Reuse finish track method
7827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7828 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7829 gtk_widget_show ( item );
7831 item = gtk_menu_item_new ();
7832 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7833 gtk_widget_show ( item );
7836 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7837 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7838 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7839 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7840 gtk_widget_show ( item );
7842 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7843 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7844 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7845 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7846 gtk_widget_show ( item );
7847 // Make it available only when a new track *not* already in progress
7848 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7850 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7851 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7852 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7853 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7854 gtk_widget_show ( item );
7856 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7857 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7859 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7860 gtk_widget_show ( item );
7862 GtkWidget *vis_submenu = gtk_menu_new ();
7863 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7864 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7865 gtk_widget_show ( item );
7866 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7868 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7869 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7870 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7871 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7872 gtk_widget_show ( item );
7874 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7877 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7878 gtk_widget_show ( item );
7880 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7883 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7885 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7888 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7890 gtk_widget_show ( item );
7892 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7894 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7895 gtk_widget_show ( item );
7899 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7900 GtkWidget *submenu_sort = gtk_menu_new ();
7901 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7902 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7903 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7904 gtk_widget_show ( item );
7905 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7907 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7908 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7909 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7910 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7911 gtk_widget_show ( item );
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7916 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7917 gtk_widget_show ( item );
7920 GtkWidget *upload_submenu = gtk_menu_new ();
7922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7924 item = gtk_menu_item_new ();
7925 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7926 gtk_widget_show ( item );
7928 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7929 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7930 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7931 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7932 if ( l->current_track ) {
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7934 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7935 gtk_widget_show ( item );
7938 item = gtk_menu_item_new ();
7939 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7940 gtk_widget_show ( item );
7943 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7944 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7946 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7947 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7949 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7950 gtk_widget_show ( item );
7952 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7955 gtk_widget_show ( item );
7957 GtkWidget *goto_submenu;
7958 goto_submenu = gtk_menu_new ();
7959 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7961 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7962 gtk_widget_show ( item );
7963 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7965 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7968 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7969 gtk_widget_show ( item );
7971 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7972 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7974 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7975 gtk_widget_show ( item );
7977 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7980 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7981 gtk_widget_show ( item );
7983 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7986 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7987 gtk_widget_show ( item );
7989 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7990 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7992 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7993 gtk_widget_show ( item );
7995 // Routes don't have speeds
7996 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7998 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8001 gtk_widget_show ( item );
8004 GtkWidget *combine_submenu;
8005 combine_submenu = gtk_menu_new ();
8006 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8007 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8008 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8009 gtk_widget_show ( item );
8010 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8012 // Routes don't have times or segments...
8013 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8014 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8016 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8017 gtk_widget_show ( item );
8019 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8021 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8022 gtk_widget_show ( item );
8025 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8027 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8028 gtk_widget_show ( item );
8030 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8031 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8033 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8035 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8036 gtk_widget_show ( item );
8038 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8039 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8041 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8043 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8044 gtk_widget_show ( item );
8046 GtkWidget *split_submenu;
8047 split_submenu = gtk_menu_new ();
8048 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8049 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8050 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8051 gtk_widget_show ( item );
8052 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8054 // Routes don't have times or segments...
8055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8056 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8058 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8059 gtk_widget_show ( item );
8061 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8062 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8064 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8065 gtk_widget_show ( item );
8068 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8069 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8070 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8071 gtk_widget_show ( item );
8073 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8075 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8076 gtk_widget_show ( item );
8077 // Make it available only when a trackpoint is selected.
8078 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8080 GtkWidget *insert_submenu = gtk_menu_new ();
8081 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8082 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8083 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8084 gtk_widget_show ( item );
8085 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8087 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8089 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8090 gtk_widget_show ( item );
8091 // Make it available only when a point is selected
8092 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8094 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8096 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8097 gtk_widget_show ( item );
8098 // Make it available only when a point is selected
8099 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8101 GtkWidget *delete_submenu;
8102 delete_submenu = gtk_menu_new ();
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8105 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8106 gtk_widget_show ( item );
8107 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8109 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8110 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8111 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8112 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8113 gtk_widget_show ( item );
8114 // Make it available only when a point is selected
8115 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8117 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8119 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8120 gtk_widget_show ( item );
8122 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8124 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8125 gtk_widget_show ( item );
8127 GtkWidget *transform_submenu;
8128 transform_submenu = gtk_menu_new ();
8129 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8131 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8132 gtk_widget_show ( item );
8133 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8135 GtkWidget *dem_submenu;
8136 dem_submenu = gtk_menu_new ();
8137 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8138 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
8139 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8140 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8142 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8144 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8145 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8146 gtk_widget_show ( item );
8148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8150 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8151 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8152 gtk_widget_show ( item );
8154 GtkWidget *smooth_submenu;
8155 smooth_submenu = gtk_menu_new ();
8156 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8157 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8158 gtk_widget_show ( item );
8159 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8161 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8163 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8164 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8165 gtk_widget_show ( item );
8167 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8169 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8170 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8171 gtk_widget_show ( item );
8173 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8174 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8176 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8179 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8180 gtk_widget_show ( item );
8182 // Routes don't have timestamps - so this is only available for tracks
8183 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8184 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8186 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8187 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8188 gtk_widget_show ( item );
8191 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8192 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8197 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8198 gtk_widget_show ( item );
8200 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8201 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8204 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8205 gtk_widget_show ( item );
8208 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8210 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8211 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8213 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8214 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
8215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8216 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8217 gtk_widget_show ( item );
8220 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8221 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8223 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8224 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8225 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8226 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8227 gtk_widget_show ( item );
8229 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8230 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8232 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8234 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8235 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8236 gtk_widget_show ( item );
8238 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8239 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8240 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
8241 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8242 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8243 gtk_widget_show ( item );
8246 // ATM can't upload a single waypoint but can do waypoints to a GPS
8247 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8248 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8249 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8250 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8251 gtk_widget_show ( item );
8252 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8254 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8255 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8256 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8257 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8258 gtk_widget_show ( item );
8262 // Only made available if a suitable program is installed
8263 if ( have_diary_program ) {
8264 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8265 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8266 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8267 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8268 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8269 gtk_widget_show ( item );
8273 #ifdef VIK_CONFIG_GOOGLE
8274 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8276 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8279 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8280 gtk_widget_show ( item );
8284 // Some things aren't usable with routes
8285 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8286 #ifdef VIK_CONFIG_OPENSTREETMAP
8287 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8288 // Convert internal pointer into track
8289 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8290 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8292 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8293 gtk_widget_show ( item );
8296 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8299 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8300 gtk_widget_show ( item );
8302 /* ATM This function is only available via the layers panel, due to needing a vlp */
8304 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8305 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8306 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8308 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8309 gtk_widget_show ( item );
8313 #ifdef VIK_CONFIG_GEOTAG
8314 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8316 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8317 gtk_widget_show ( item );
8321 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8322 // Only show on viewport popmenu when a trackpoint is selected
8323 if ( ! vlp && l->current_tpl ) {
8325 item = gtk_menu_item_new ();
8326 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8327 gtk_widget_show ( item );
8329 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8330 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8331 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8332 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8333 gtk_widget_show ( item );
8337 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8338 GtkWidget *transform_submenu;
8339 transform_submenu = gtk_menu_new ();
8340 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8342 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8343 gtk_widget_show ( item );
8344 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8346 GtkWidget *dem_submenu;
8347 dem_submenu = gtk_menu_new ();
8348 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8349 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
8350 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8351 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8355 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8356 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8357 gtk_widget_show ( item );
8359 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8360 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8361 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8362 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8363 gtk_widget_show ( item );
8366 gtk_widget_show_all ( GTK_WIDGET(menu) );
8371 // TODO: Probably better to rework this track manipulation in viktrack.c
8372 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8375 if (!vtl->current_tpl)
8378 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8379 VikTrackpoint *tp_other = NULL;
8382 if (!vtl->current_tpl->prev)
8384 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8386 if (!vtl->current_tpl->next)
8388 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8391 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8394 VikTrackpoint *tp_new = vik_trackpoint_new();
8395 struct LatLon ll_current, ll_other;
8396 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8397 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8399 /* main positional interpolation */
8400 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8401 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8403 /* Now other properties that can be interpolated */
8404 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8406 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8407 /* Note here the division is applied to each part, then added
8408 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8409 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8410 tp_new->has_timestamp = TRUE;
8413 if (tp_current->speed != NAN && tp_other->speed != NAN)
8414 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8416 /* TODO - improve interpolation of course, as it may not be correct.
8417 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8418 [similar applies if value is in radians] */
8419 if (tp_current->course != NAN && tp_other->course != NAN)
8420 tp_new->course = (tp_current->course + tp_other->course)/2;
8422 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8424 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8425 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8427 // Otherwise try routes
8428 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8432 gint index = g_list_index ( trk->trackpoints, tp_current );
8436 // NB no recalculation of bounds since it is inserted between points
8437 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8442 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8448 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8452 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8454 if ( vtl->current_tpl )
8456 vtl->current_tpl = NULL;
8457 vtl->current_tp_track = NULL;
8458 vtl->current_tp_id = NULL;
8459 vik_layer_emit_update(VIK_LAYER(vtl));
8463 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8465 g_assert ( vtl->tpwin != NULL );
8466 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8467 trw_layer_cancel_current_tp ( vtl, TRUE );
8469 if ( vtl->current_tpl == NULL )
8472 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8474 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8475 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8477 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8479 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8481 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8485 trw_layer_trackpoint_selected_delete ( vtl, tr );
8487 if ( vtl->current_tpl )
8488 // Reset dialog with the available adjacent trackpoint
8489 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8491 vik_layer_emit_update(VIK_LAYER(vtl));
8493 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8495 if ( vtl->current_tp_track )
8496 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8497 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8499 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8501 if ( vtl->current_tp_track )
8502 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8503 vik_layer_emit_update(VIK_LAYER(vtl));
8505 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8507 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8508 vik_layer_emit_update(VIK_LAYER(vtl));
8510 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8511 vik_layer_emit_update(VIK_LAYER(vtl));
8515 * trw_layer_dialog_shift:
8516 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8518 * Try to reposition a dialog if it's over the specified coord
8519 * so to not obscure the item of interest
8521 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8523 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8525 // Attempt force dialog to be shown so we can find out where it is more reliably...
8526 while ( gtk_events_pending() )
8527 gtk_main_iteration ();
8529 // get parent window position & size
8530 gint win_pos_x, win_pos_y;
8531 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8533 gint win_size_x, win_size_y;
8534 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8536 // get own dialog size
8537 gint dia_size_x, dia_size_y;
8538 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8540 // get own dialog position
8541 gint dia_pos_x, dia_pos_y;
8542 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8544 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8545 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8547 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8549 gint vp_xx, vp_yy; // In viewport pixels
8550 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8552 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8556 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8558 // Transform Viewport pixels into absolute pixels
8559 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8560 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8562 // Is dialog over the point (to within an ^^ edge value)
8563 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8564 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8568 gint hh = vik_viewport_get_height ( vvp );
8570 // Consider the difference in viewport to the full window
8571 gint offset_y = dest_y;
8572 // Add difference between dialog and window sizes
8573 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8575 if ( vp_yy > hh/2 ) {
8576 // Point in bottom half, move window to top half
8577 gtk_window_move ( dialog, dia_pos_x, offset_y );
8580 // Point in top half, move dialog down
8581 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8585 // Shift left<->right
8586 gint ww = vik_viewport_get_width ( vvp );
8588 // Consider the difference in viewport to the full window
8589 gint offset_x = dest_x;
8590 // Add difference between dialog and window sizes
8591 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8593 if ( vp_xx > ww/2 ) {
8594 // Point on right, move window to left
8595 gtk_window_move ( dialog, offset_x, dia_pos_y );
8598 // Point on left, move right
8599 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8607 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8611 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8612 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8613 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8614 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8616 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8618 if ( vtl->current_tpl ) {
8619 // get tp pixel position
8620 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8622 // Shift up<->down to try not to obscure the trackpoint.
8623 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8627 if ( vtl->current_tpl )
8628 if ( vtl->current_tp_track )
8629 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8630 /* set layer name and TP data */
8633 /***************************************************************************
8635 ***************************************************************************/
8637 /*** Utility data structures and functions ****/
8641 gint closest_x, closest_y;
8642 gboolean draw_images;
8643 gpointer *closest_wp_id;
8644 VikWaypoint *closest_wp;
8650 gint closest_x, closest_y;
8651 gpointer closest_track_id;
8652 VikTrackpoint *closest_tp;
8658 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8664 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8666 // If waypoint has an image then use the image size to select
8667 if ( params->draw_images && wp->image ) {
8668 gint slackx, slacky;
8669 slackx = wp->image_width / 2;
8670 slacky = wp->image_height / 2;
8672 if ( x <= params->x + slackx && x >= params->x - slackx
8673 && y <= params->y + slacky && y >= params->y - slacky ) {
8674 params->closest_wp_id = id;
8675 params->closest_wp = wp;
8676 params->closest_x = x;
8677 params->closest_y = y;
8680 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8681 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8682 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8684 params->closest_wp_id = id;
8685 params->closest_wp = wp;
8686 params->closest_x = x;
8687 params->closest_y = y;
8691 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8693 GList *tpl = t->trackpoints;
8699 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8705 tp = VIK_TRACKPOINT(tpl->data);
8707 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8709 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8710 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8711 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8713 params->closest_track_id = id;
8714 params->closest_tp = tp;
8715 params->closest_tpl = tpl;
8716 params->closest_x = x;
8717 params->closest_y = y;
8723 // ATM: Leave this as 'Track' only.
8724 // Not overly bothered about having a snap to route trackpoint capability
8725 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8727 TPSearchParams params;
8731 params.closest_track_id = NULL;
8732 params.closest_tp = NULL;
8733 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8734 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8735 return params.closest_tp;
8738 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8740 WPSearchParams params;
8744 params.draw_images = vtl->drawimages;
8745 params.closest_wp = NULL;
8746 params.closest_wp_id = NULL;
8747 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8748 return params.closest_wp;
8752 // Some forward declarations
8753 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8754 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8755 static void marker_end_move ( tool_ed_t *t );
8758 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8762 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8764 // Here always allow snapping back to the original location
8765 // this is useful when one decides not to move the thing afterall
8766 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8769 if ( event->state & GDK_CONTROL_MASK )
8771 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8773 new_coord = tp->coord;
8777 if ( event->state & GDK_SHIFT_MASK )
8779 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8781 new_coord = wp->coord;
8785 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8787 marker_moveto ( t, x, y );
8794 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8796 if ( t->holding && event->button == 1 )
8799 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8802 if ( event->state & GDK_CONTROL_MASK )
8804 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8806 new_coord = tp->coord;
8810 if ( event->state & GDK_SHIFT_MASK )
8812 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8814 new_coord = wp->coord;
8817 marker_end_move ( t );
8819 // Determine if working on a waypoint or a trackpoint
8820 if ( t->is_waypoint ) {
8821 // Update waypoint position
8822 vtl->current_wp->coord = new_coord;
8823 trw_layer_calculate_bounds_waypoints ( vtl );
8824 // Reset waypoint pointer
8825 vtl->current_wp = NULL;
8826 vtl->current_wp_id = NULL;
8829 if ( vtl->current_tpl ) {
8830 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8832 if ( vtl->current_tp_track )
8833 vik_track_calculate_bounds ( vtl->current_tp_track );
8836 if ( vtl->current_tp_track )
8837 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8838 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8842 vik_layer_emit_update ( VIK_LAYER(vtl) );
8849 Returns true if a waypoint or track is found near the requested event position for this particular layer
8850 The item found is automatically selected
8851 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8853 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8855 if ( event->button != 1 )
8858 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8861 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8865 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8867 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8869 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8870 WPSearchParams wp_params;
8871 wp_params.vvp = vvp;
8872 wp_params.x = event->x;
8873 wp_params.y = event->y;
8874 wp_params.draw_images = vtl->drawimages;
8875 wp_params.closest_wp_id = NULL;
8876 wp_params.closest_wp = NULL;
8878 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8880 if ( wp_params.closest_wp ) {
8883 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8885 // Too easy to move it so must be holding shift to start immediately moving it
8886 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8887 if ( event->state & GDK_SHIFT_MASK ||
8888 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8889 // Put into 'move buffer'
8890 // NB vvp & vw already set in tet
8891 tet->vtl = (gpointer)vtl;
8892 tet->is_waypoint = TRUE;
8894 marker_begin_move (tet, event->x, event->y);
8897 vtl->current_wp = wp_params.closest_wp;
8898 vtl->current_wp_id = wp_params.closest_wp_id;
8900 if ( event->type == GDK_2BUTTON_PRESS ) {
8901 if ( vtl->current_wp->image ) {
8902 menu_array_sublayer values;
8903 values[MA_VTL] = vtl;
8904 values[MA_MISC] = vtl->current_wp->image;
8905 trw_layer_show_picture ( values );
8909 vik_layer_emit_update ( VIK_LAYER(vtl) );
8915 // Used for both track and route lists
8916 TPSearchParams tp_params;
8917 tp_params.vvp = vvp;
8918 tp_params.x = event->x;
8919 tp_params.y = event->y;
8920 tp_params.closest_track_id = NULL;
8921 tp_params.closest_tp = NULL;
8922 tp_params.closest_tpl = NULL;
8923 tp_params.bbox = bbox;
8925 if (vtl->tracks_visible) {
8926 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8928 if ( tp_params.closest_tp ) {
8930 // Always select + highlight the track
8931 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8933 tet->is_waypoint = FALSE;
8935 // Select the Trackpoint
8936 // Can move it immediately when control held or it's the previously selected tp
8937 if ( event->state & GDK_CONTROL_MASK ||
8938 vtl->current_tpl == tp_params.closest_tpl ) {
8939 // Put into 'move buffer'
8940 // NB vvp & vw already set in tet
8941 tet->vtl = (gpointer)vtl;
8942 marker_begin_move (tet, event->x, event->y);
8945 vtl->current_tpl = tp_params.closest_tpl;
8946 vtl->current_tp_id = tp_params.closest_track_id;
8947 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8949 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8952 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8954 vik_layer_emit_update ( VIK_LAYER(vtl) );
8959 // Try again for routes
8960 if (vtl->routes_visible) {
8961 g_hash_table_foreach ( vtl->routes, (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->routes_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->routes, 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 );
8989 vik_layer_emit_update ( VIK_LAYER(vtl) );
8994 /* these aren't the droids you're looking for */
8995 vtl->current_wp = NULL;
8996 vtl->current_wp_id = NULL;
8997 trw_layer_cancel_current_tp ( vtl, FALSE );
9000 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9005 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9007 if ( event->button != 3 )
9010 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9013 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9016 /* Post menu for the currently selected item */
9018 /* See if a track is selected */
9019 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9020 if ( track && track->visible ) {
9022 if ( track->name ) {
9024 if ( vtl->track_right_click_menu )
9025 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9027 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9034 if ( track->is_route )
9035 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9037 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9039 if ( trkf && udataU.uuid ) {
9042 if ( track->is_route )
9043 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9045 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9047 trw_layer_sublayer_add_menu_items ( vtl,
9048 vtl->track_right_click_menu,
9050 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9056 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9062 /* See if a waypoint is selected */
9063 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9064 if ( waypoint && waypoint->visible ) {
9065 if ( waypoint->name ) {
9067 if ( vtl->wp_right_click_menu )
9068 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9070 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9073 udata.wp = waypoint;
9076 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9078 if ( wpf && udata.uuid ) {
9079 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9081 trw_layer_sublayer_add_menu_items ( vtl,
9082 vtl->wp_right_click_menu,
9084 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9089 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9098 /* background drawing hook, to be passed the viewport */
9099 static gboolean tool_sync_done = TRUE;
9101 static gboolean tool_sync(gpointer data)
9103 VikViewport *vvp = data;
9104 gdk_threads_enter();
9105 vik_viewport_sync(vvp);
9106 tool_sync_done = TRUE;
9107 gdk_threads_leave();
9111 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9114 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9115 gdk_gc_set_function ( t->gc, GDK_INVERT );
9116 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9117 vik_viewport_sync(t->vvp);
9122 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9124 VikViewport *vvp = t->vvp;
9125 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9126 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9130 if (tool_sync_done) {
9131 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9132 tool_sync_done = FALSE;
9136 static void marker_end_move ( tool_ed_t *t )
9138 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9139 g_object_unref ( t->gc );
9143 /*** Edit waypoint ****/
9145 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9147 tool_ed_t *t = g_new(tool_ed_t, 1);
9153 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9158 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9160 WPSearchParams params;
9161 tool_ed_t *t = data;
9162 VikViewport *vvp = t->vvp;
9164 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9171 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9174 if ( vtl->current_wp && vtl->current_wp->visible )
9176 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9178 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9180 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9181 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9183 if ( event->button == 3 )
9184 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9186 marker_begin_move(t, event->x, event->y);
9193 params.x = event->x;
9194 params.y = event->y;
9195 params.draw_images = vtl->drawimages;
9196 params.closest_wp_id = NULL;
9197 params.closest_wp = NULL;
9198 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9199 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9201 marker_begin_move(t, event->x, event->y);
9204 else if ( params.closest_wp )
9206 if ( event->button == 3 )
9207 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9209 vtl->waypoint_rightclick = FALSE;
9211 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9213 vtl->current_wp = params.closest_wp;
9214 vtl->current_wp_id = params.closest_wp_id;
9216 /* could make it so don't update if old WP is off screen and new is null but oh well */
9217 vik_layer_emit_update ( VIK_LAYER(vtl) );
9221 vtl->current_wp = NULL;
9222 vtl->current_wp_id = NULL;
9223 vtl->waypoint_rightclick = FALSE;
9224 vik_layer_emit_update ( VIK_LAYER(vtl) );
9228 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9230 tool_ed_t *t = data;
9231 VikViewport *vvp = t->vvp;
9233 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9238 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9241 if ( event->state & GDK_CONTROL_MASK )
9243 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9245 new_coord = tp->coord;
9249 if ( event->state & GDK_SHIFT_MASK )
9251 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9252 if ( wp && wp != vtl->current_wp )
9253 new_coord = wp->coord;
9258 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9260 marker_moveto ( t, x, y );
9267 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9269 tool_ed_t *t = data;
9270 VikViewport *vvp = t->vvp;
9272 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9275 if ( t->holding && event->button == 1 )
9278 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9281 if ( event->state & GDK_CONTROL_MASK )
9283 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9285 new_coord = tp->coord;
9289 if ( event->state & GDK_SHIFT_MASK )
9291 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9292 if ( wp && wp != vtl->current_wp )
9293 new_coord = wp->coord;
9296 marker_end_move ( t );
9298 vtl->current_wp->coord = new_coord;
9300 trw_layer_calculate_bounds_waypoints ( vtl );
9301 vik_layer_emit_update ( VIK_LAYER(vtl) );
9304 /* PUT IN RIGHT PLACE!!! */
9305 if ( event->button == 3 && vtl->waypoint_rightclick )
9307 if ( vtl->wp_right_click_menu )
9308 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9309 if ( vtl->current_wp ) {
9310 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9311 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 );
9312 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9314 vtl->waypoint_rightclick = FALSE;
9319 /*** New track ****/
9321 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9328 GdkDrawable *drawable;
9334 * Draw specified pixmap
9336 static gboolean draw_sync ( gpointer data )
9338 draw_sync_t *ds = (draw_sync_t*) data;
9339 // Sometimes don't want to draw
9340 // normally because another update has taken precedent such as panning the display
9341 // which means this pixmap is no longer valid
9342 if ( ds->vtl->draw_sync_do ) {
9343 gdk_threads_enter();
9344 gdk_draw_drawable (ds->drawable,
9347 0, 0, 0, 0, -1, -1);
9348 ds->vtl->draw_sync_done = TRUE;
9349 gdk_threads_leave();
9355 static gchar* distance_string (gdouble distance)
9359 /* draw label with distance */
9360 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9361 switch (dist_units) {
9362 case VIK_UNITS_DISTANCE_MILES:
9363 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9364 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9365 } else if (distance < 1609.4) {
9366 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9368 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9372 // VIK_UNITS_DISTANCE_KILOMETRES
9373 if (distance >= 1000 && distance < 100000) {
9374 g_sprintf(str, "%3.2f km", distance/1000.0);
9375 } else if (distance < 1000) {
9376 g_sprintf(str, "%d m", (int)distance);
9378 g_sprintf(str, "%d km", (int)distance/1000);
9382 return g_strdup (str);
9386 * Actually set the message in statusbar
9388 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9390 // Only show elevation data when track has some elevation properties
9391 gchar str_gain_loss[64];
9392 str_gain_loss[0] = '\0';
9393 gchar str_last_step[64];
9394 str_last_step[0] = '\0';
9395 gchar *str_total = distance_string (distance);
9397 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9398 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9399 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9401 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9404 if ( last_step > 0 ) {
9405 gchar *tmp = distance_string (last_step);
9406 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9410 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9412 // Write with full gain/loss information
9413 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9414 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9416 g_free ( str_total );
9420 * Figure out what information should be set in the statusbar and then write it
9422 static void update_statusbar ( VikTrwLayer *vtl )
9424 // Get elevation data
9425 gdouble elev_gain, elev_loss;
9426 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9428 /* Find out actual distance of current track */
9429 gdouble distance = vik_track_get_length (vtl->current_track);
9431 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9435 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9437 /* if we haven't sync'ed yet, we don't have time to do more. */
9438 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9439 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9441 static GdkPixmap *pixmap = NULL;
9443 // Need to check in case window has been resized
9444 w1 = vik_viewport_get_width(vvp);
9445 h1 = vik_viewport_get_height(vvp);
9447 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9449 gdk_drawable_get_size (pixmap, &w2, &h2);
9450 if (w1 != w2 || h1 != h2) {
9451 g_object_unref ( G_OBJECT ( pixmap ) );
9452 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9455 // Reset to background
9456 gdk_draw_drawable (pixmap,
9457 vtl->current_track_newpoint_gc,
9458 vik_viewport_get_pixmap(vvp),
9459 0, 0, 0, 0, -1, -1);
9461 draw_sync_t *passalong;
9464 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9466 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9467 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9468 // thus when we come to reset to the background it would include what we have already drawn!!
9469 gdk_draw_line ( pixmap,
9470 vtl->current_track_newpoint_gc,
9471 x1, y1, event->x, event->y );
9472 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9474 /* Find out actual distance of current track */
9475 gdouble distance = vik_track_get_length (vtl->current_track);
9477 // Now add distance to where the pointer is //
9480 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9481 vik_coord_to_latlon ( &coord, &ll );
9482 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9483 distance = distance + last_step;
9485 // Get elevation data
9486 gdouble elev_gain, elev_loss;
9487 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9489 // Adjust elevation data (if available) for the current pointer position
9491 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9492 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9493 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9494 // Adjust elevation of last track point
9495 if ( elev_new > last_tpt->altitude )
9497 elev_gain += elev_new - last_tpt->altitude;
9500 elev_loss += last_tpt->altitude - elev_new;
9505 // Display of the distance 'tooltip' during track creation is controlled by a preference
9507 if ( a_vik_get_create_track_tooltip() ) {
9509 gchar *str = distance_string (distance);
9511 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9512 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9513 pango_layout_set_text (pl, str, -1);
9515 pango_layout_get_pixel_size ( pl, &wd, &hd );
9518 // offset from cursor a bit depending on font size
9522 // Create a background block to make the text easier to read over the background map
9523 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9524 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9525 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9527 g_object_unref ( G_OBJECT ( pl ) );
9528 g_object_unref ( G_OBJECT ( background_block_gc ) );
9532 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9533 passalong->vtl = vtl;
9534 passalong->pixmap = pixmap;
9535 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9536 passalong->gc = vtl->current_track_newpoint_gc;
9540 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9542 // Update statusbar with full gain/loss information
9543 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9545 // draw pixmap when we have time to
9546 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9547 vtl->draw_sync_done = FALSE;
9548 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9550 return VIK_LAYER_TOOL_ACK;
9553 // NB vtl->current_track must be valid
9554 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9557 if ( vtl->current_track->trackpoints ) {
9558 // TODO rework this...
9559 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9560 GList *last = g_list_last(vtl->current_track->trackpoints);
9561 g_free ( last->data );
9562 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9564 vik_track_calculate_bounds ( vtl->current_track );
9568 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9570 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9571 vtl->current_track = NULL;
9572 vik_layer_emit_update ( VIK_LAYER(vtl) );
9574 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9575 undo_trackpoint_add ( vtl );
9576 update_statusbar ( vtl );
9577 vik_layer_emit_update ( VIK_LAYER(vtl) );
9584 * Common function to handle trackpoint button requests on either a route or a track
9585 * . enables adding a point via normal click
9586 * . enables removal of last point via right click
9587 * . finishing of the track or route via double clicking
9589 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9593 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9596 if ( event->button == 2 ) {
9597 // As the display is panning, the new track pixmap is now invalid so don't draw it
9598 // otherwise this drawing done results in flickering back to an old image
9599 vtl->draw_sync_do = FALSE;
9603 if ( event->button == 3 )
9605 if ( !vtl->current_track )
9607 undo_trackpoint_add ( vtl );
9608 update_statusbar ( vtl );
9609 vik_layer_emit_update ( VIK_LAYER(vtl) );
9613 if ( event->type == GDK_2BUTTON_PRESS )
9615 /* subtract last (duplicate from double click) tp then end */
9616 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9618 /* undo last, then end */
9619 undo_trackpoint_add ( vtl );
9620 vtl->current_track = NULL;
9622 vik_layer_emit_update ( VIK_LAYER(vtl) );
9626 tp = vik_trackpoint_new();
9627 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9629 /* snap to other TP */
9630 if ( event->state & GDK_CONTROL_MASK )
9632 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9634 tp->coord = other_tp->coord;
9637 tp->newsegment = FALSE;
9638 tp->has_timestamp = FALSE;
9641 if ( vtl->current_track ) {
9642 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9643 /* Auto attempt to get elevation from DEM data (if it's available) */
9644 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9647 vtl->ct_x1 = vtl->ct_x2;
9648 vtl->ct_y1 = vtl->ct_y2;
9649 vtl->ct_x2 = event->x;
9650 vtl->ct_y2 = event->y;
9652 vik_layer_emit_update ( VIK_LAYER(vtl) );
9656 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9658 // ----------------------------------------------------- if current is a route - switch to new track
9659 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9661 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9662 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9664 new_track_create_common ( vtl, name );
9670 return tool_new_track_or_route_click ( vtl, event, vvp );
9673 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9675 if ( event->button == 2 ) {
9676 // Pan moving ended - enable potential point drawing again
9677 vtl->draw_sync_do = TRUE;
9678 vtl->draw_sync_done = TRUE;
9682 /*** New route ****/
9684 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9689 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9691 // -------------------------- if current is a track - switch to new route
9692 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9694 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9695 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9696 new_route_create_common ( vtl, name );
9702 return tool_new_track_or_route_click ( vtl, event, vvp );
9705 /*** New waypoint ****/
9707 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9712 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9715 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9717 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9718 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9719 trw_layer_calculate_bounds_waypoints ( vtl );
9720 vik_layer_emit_update ( VIK_LAYER(vtl) );
9726 /*** Edit trackpoint ****/
9728 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9730 tool_ed_t *t = g_new(tool_ed_t, 1);
9736 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9741 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9743 tool_ed_t *t = data;
9744 VikViewport *vvp = t->vvp;
9745 TPSearchParams params;
9746 /* OUTDATED DOCUMENTATION:
9747 find 5 pixel range on each side. then put these UTM, and a pointer
9748 to the winning track name (and maybe the winning track itself), and a
9749 pointer to the winning trackpoint, inside an array or struct. pass
9750 this along, do a foreach on the tracks which will do a foreach on the
9753 params.x = event->x;
9754 params.y = event->y;
9755 params.closest_track_id = NULL;
9756 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9757 params.closest_tp = NULL;
9758 params.closest_tpl = NULL;
9759 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9761 if ( event->button != 1 )
9764 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9767 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9770 if ( vtl->current_tpl )
9772 /* 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.) */
9773 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9774 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9779 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9781 if ( current_tr->visible &&
9782 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9783 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9784 marker_begin_move ( t, event->x, event->y );
9790 if ( vtl->tracks_visible )
9791 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9793 if ( params.closest_tp )
9795 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9796 vtl->current_tpl = params.closest_tpl;
9797 vtl->current_tp_id = params.closest_track_id;
9798 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9799 trw_layer_tpwin_init ( vtl );
9800 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9801 vik_layer_emit_update ( VIK_LAYER(vtl) );
9805 if ( vtl->routes_visible )
9806 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9808 if ( params.closest_tp )
9810 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9811 vtl->current_tpl = params.closest_tpl;
9812 vtl->current_tp_id = params.closest_track_id;
9813 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9814 trw_layer_tpwin_init ( vtl );
9815 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9816 vik_layer_emit_update ( VIK_LAYER(vtl) );
9820 /* these aren't the droids you're looking for */
9824 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9826 tool_ed_t *t = data;
9827 VikViewport *vvp = t->vvp;
9829 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9835 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9838 if ( event->state & GDK_CONTROL_MASK )
9840 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9841 if ( tp && tp != vtl->current_tpl->data )
9842 new_coord = tp->coord;
9844 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9847 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9848 marker_moveto ( t, x, y );
9856 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9858 tool_ed_t *t = data;
9859 VikViewport *vvp = t->vvp;
9861 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9863 if ( event->button != 1)
9868 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9871 if ( event->state & GDK_CONTROL_MASK )
9873 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9874 if ( tp && tp != vtl->current_tpl->data )
9875 new_coord = tp->coord;
9878 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9879 if ( vtl->current_tp_track )
9880 vik_track_calculate_bounds ( vtl->current_tp_track );
9882 marker_end_move ( t );
9884 /* diff dist is diff from orig */
9886 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9888 vik_layer_emit_update ( VIK_LAYER(vtl) );
9895 /*** Route Finder ***/
9896 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9901 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9904 if ( !vtl ) return FALSE;
9905 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9906 if ( event->button == 3 && vtl->route_finder_current_track ) {
9908 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9910 vtl->route_finder_coord = *new_end;
9912 vik_layer_emit_update ( VIK_LAYER(vtl) );
9913 /* remove last ' to:...' */
9914 if ( vtl->route_finder_current_track->comment ) {
9915 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9916 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9917 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9918 last_to - vtl->route_finder_current_track->comment - 1);
9919 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9924 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9925 struct LatLon start, end;
9927 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9928 vik_coord_to_latlon ( &(tmp), &end );
9929 vtl->route_finder_coord = tmp; /* for continuations */
9931 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9932 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9933 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9935 vtl->route_finder_check_added_track = TRUE;
9936 vtl->route_finder_started = FALSE;
9939 vik_routing_default_find ( vtl, start, end);
9941 /* see if anything was done -- a track was added or appended to */
9942 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9943 vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
9944 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9945 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9946 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9947 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9950 if ( vtl->route_finder_added_track )
9951 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9953 vtl->route_finder_added_track = NULL;
9954 vtl->route_finder_check_added_track = FALSE;
9955 vtl->route_finder_append = FALSE;
9957 vik_layer_emit_update ( VIK_LAYER(vtl) );
9959 vtl->route_finder_started = TRUE;
9960 vtl->route_finder_coord = tmp;
9961 vtl->route_finder_current_track = NULL;
9966 /*** Show picture ****/
9968 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9973 /* Params are: vvp, event, last match found or NULL */
9974 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9976 if ( wp->image && wp->visible )
9978 gint x, y, slackx, slacky;
9979 GdkEventButton *event = (GdkEventButton *) params[1];
9981 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9982 slackx = wp->image_width / 2;
9983 slacky = wp->image_height / 2;
9984 if ( x <= event->x + slackx && x >= event->x - slackx
9985 && y <= event->y + slacky && y >= event->y - slacky )
9987 params[2] = wp->image; /* we've found a match. however continue searching
9988 * since we want to find the last match -- that
9989 * is, the match that was drawn last. */
9994 static void trw_layer_show_picture ( menu_array_sublayer values )
9996 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9998 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10000 GError *err = NULL;
10001 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10002 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10003 g_free ( quoted_file );
10004 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10006 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() );
10007 g_error_free ( err );
10010 #endif /* WINDOWS */
10013 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10015 gpointer params[3] = { vvp, event, NULL };
10016 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10018 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10021 static menu_array_sublayer values;
10022 values[MA_VTL] = vtl;
10023 values[MA_MISC] = params[2];
10024 trw_layer_show_picture ( values );
10025 return TRUE; /* found a match */
10028 return FALSE; /* go through other layers, searching for a match */
10031 /***************************************************************************
10033 ***************************************************************************/
10036 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10038 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10039 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10042 /* Structure for thumbnail creating data used in the background thread */
10044 VikTrwLayer *vtl; // Layer needed for redrawing
10045 GSList *pics; // Image list
10046 } thumbnail_create_thread_data;
10048 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10050 guint total = g_slist_length(tctd->pics), done = 0;
10051 while ( tctd->pics )
10053 a_thumbnails_create ( (gchar *) tctd->pics->data );
10054 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10056 return -1; /* Abort thread */
10058 tctd->pics = tctd->pics->next;
10061 // Redraw to show the thumbnails as they are now created
10062 if ( IS_VIK_LAYER(tctd->vtl) )
10063 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10068 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10070 while ( tctd->pics )
10072 g_free ( tctd->pics->data );
10073 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10078 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10080 if ( ! vtl->has_verified_thumbnails )
10082 GSList *pics = NULL;
10083 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10086 gint len = g_slist_length ( pics );
10087 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10088 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10091 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10093 (vik_thr_func) create_thumbnails_thread,
10095 (vik_thr_free_func) thumbnail_create_thread_free,
10103 static const gchar* my_track_colors ( gint ii )
10105 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10117 // Fast and reliable way of returning a colour
10118 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10121 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10123 GHashTableIter iter;
10124 gpointer key, value;
10128 g_hash_table_iter_init ( &iter, vtl->tracks );
10130 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10132 // Tracks get a random spread of colours if not already assigned
10133 if ( ! VIK_TRACK(value)->has_color ) {
10134 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10135 VIK_TRACK(value)->color = vtl->track_color;
10137 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10139 VIK_TRACK(value)->has_color = TRUE;
10142 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10145 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10151 g_hash_table_iter_init ( &iter, vtl->routes );
10153 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10155 // Routes get an intermix of reds
10156 if ( ! VIK_TRACK(value)->has_color ) {
10158 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10160 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10161 VIK_TRACK(value)->has_color = TRUE;
10164 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10171 * (Re)Calculate the bounds of the waypoints in this layer,
10172 * This should be called whenever waypoints are changed
10174 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10176 struct LatLon topleft = { 0.0, 0.0 };
10177 struct LatLon bottomright = { 0.0, 0.0 };
10180 GHashTableIter iter;
10181 gpointer key, value;
10183 g_hash_table_iter_init ( &iter, vtl->waypoints );
10185 // Set bounds to first point
10186 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10187 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10188 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10191 // Ensure there is another point...
10192 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10194 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10196 // See if this point increases the bounds.
10197 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10199 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10200 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10201 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10202 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10206 vtl->waypoints_bbox.north = topleft.lat;
10207 vtl->waypoints_bbox.east = bottomright.lon;
10208 vtl->waypoints_bbox.south = bottomright.lat;
10209 vtl->waypoints_bbox.west = topleft.lon;
10212 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10214 vik_track_calculate_bounds ( trk );
10217 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10219 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10220 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10223 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10225 if ( ! VIK_LAYER(vtl)->vt )
10228 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10229 if ( g_hash_table_size (vtl->tracks) > 1 )
10230 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10232 if ( g_hash_table_size (vtl->routes) > 1 )
10233 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10235 if ( g_hash_table_size (vtl->waypoints) > 1 )
10236 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10239 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10241 if ( VIK_LAYER(vtl)->realized )
10242 trw_layer_verify_thumbnails ( vtl, vvp );
10243 trw_layer_track_alloc_colors ( vtl );
10245 trw_layer_calculate_bounds_waypoints ( vtl );
10246 trw_layer_calculate_bounds_tracks ( vtl );
10248 // Apply treeview sort after loading all the tracks for this layer
10249 // (rather than sorted insert on each individual track additional)
10250 // and after subsequent changes to the properties as the specified order may have changed.
10251 // since the sorting of a treeview section is now very quick
10252 // NB sorting is also performed after every name change as well to maintain the list order
10253 trw_layer_sort_all ( vtl );
10255 // Setting metadata time if not otherwise set
10256 if ( vtl->metadata ) {
10258 gboolean need_to_set_time = TRUE;
10259 if ( vtl->metadata->timestamp ) {
10260 need_to_set_time = FALSE;
10261 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10262 need_to_set_time = TRUE;
10265 if ( need_to_set_time ) {
10266 // Could rewrite this as a general get first time of a TRW Layer function
10267 GTimeVal timestamp;
10268 timestamp.tv_usec = 0;
10269 gboolean has_timestamp = FALSE;
10272 gl = g_hash_table_get_values ( vtl->tracks );
10273 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10274 gl = g_list_first ( gl );
10276 // Check times of tracks
10278 // Only need to check the first track as they have been sorted by time
10279 VikTrack *trk = (VikTrack*)gl->data;
10280 // Assume trackpoints already sorted by time
10281 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10282 if ( tpt && tpt->has_timestamp ) {
10283 timestamp.tv_sec = tpt->timestamp;
10284 has_timestamp = TRUE;
10286 g_list_free ( gl );
10289 if ( !has_timestamp ) {
10290 // 'Last' resort - current time
10291 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10292 g_get_current_time ( ×tamp );
10294 // Check times of waypoints
10295 gl = g_hash_table_get_values ( vtl->waypoints );
10297 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10298 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10299 if ( wpt->has_timestamp ) {
10300 if ( timestamp.tv_sec > wpt->timestamp ) {
10301 timestamp.tv_sec = wpt->timestamp;
10302 has_timestamp = TRUE;
10306 g_list_free ( gl );
10309 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10314 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10316 return vtl->coord_mode;
10320 * Uniquify the whole layer
10321 * Also requires the layers panel as the names shown there need updating too
10322 * Returns whether the operation was successful or not
10324 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10326 if ( vtl && vlp ) {
10327 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10328 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10329 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10335 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10337 vik_coord_convert ( &(wp->coord), *dest_mode );
10340 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10342 vik_track_convert ( tr, *dest_mode );
10345 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10347 if ( vtl->coord_mode != dest_mode )
10349 vtl->coord_mode = dest_mode;
10350 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10351 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10352 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10356 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10358 vtl->menu_selection = selection;
10361 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10363 return (vtl->menu_selection);
10366 /* ----------- Downloading maps along tracks --------------- */
10368 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10370 /* TODO: calculating based on current size of viewport */
10371 const gdouble w_at_zoom_0_125 = 0.0013;
10372 const gdouble h_at_zoom_0_125 = 0.0011;
10373 gdouble zoom_factor = zoom_level/0.125;
10375 wh->lat = h_at_zoom_0_125 * zoom_factor;
10376 wh->lon = w_at_zoom_0_125 * zoom_factor;
10378 return 0; /* all OK */
10381 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10383 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10384 (dist->lat >= ABS(to->north_south - from->north_south)))
10387 VikCoord *coord = g_malloc(sizeof(VikCoord));
10388 coord->mode = VIK_COORD_LATLON;
10390 if (ABS(gradient) < 1) {
10391 if (from->east_west > to->east_west)
10392 coord->east_west = from->east_west - dist->lon;
10394 coord->east_west = from->east_west + dist->lon;
10395 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10397 if (from->north_south > to->north_south)
10398 coord->north_south = from->north_south - dist->lat;
10400 coord->north_south = from->north_south + dist->lat;
10401 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10407 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10409 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10410 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10412 VikCoord *next = from;
10414 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10416 list = g_list_prepend(list, next);
10422 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10424 typedef struct _Rect {
10429 #define GLRECT(iter) ((Rect *)((iter)->data))
10432 GList *rects_to_download = NULL;
10435 if (get_download_area_width(vvp, zoom_level, &wh))
10438 GList *iter = tr->trackpoints;
10442 gboolean new_map = TRUE;
10443 VikCoord *cur_coord, tl, br;
10446 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10448 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10449 rect = g_malloc(sizeof(Rect));
10452 rect->center = *cur_coord;
10453 rects_to_download = g_list_prepend(rects_to_download, rect);
10458 gboolean found = FALSE;
10459 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10460 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10471 GList *fillins = NULL;
10472 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10473 /* seems that ATM the function get_next_coord works only for LATLON */
10474 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10475 /* fill-ins for far apart points */
10476 GList *cur_rect, *next_rect;
10477 for (cur_rect = rects_to_download;
10478 (next_rect = cur_rect->next) != NULL;
10479 cur_rect = cur_rect->next) {
10480 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10481 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10482 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10486 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10489 GList *iter = fillins;
10491 cur_coord = (VikCoord *)(iter->data);
10492 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10493 rect = g_malloc(sizeof(Rect));
10496 rect->center = *cur_coord;
10497 rects_to_download = g_list_prepend(rects_to_download, rect);
10502 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10503 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10507 for (iter = fillins; iter; iter = iter->next)
10508 g_free(iter->data);
10509 g_list_free(fillins);
10511 if (rects_to_download) {
10512 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10513 g_free(rect_iter->data);
10514 g_list_free(rects_to_download);
10518 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10522 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10523 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10524 gint selected_zoom, default_zoom;
10526 VikTrwLayer *vtl = values[MA_VTL];
10527 VikLayersPanel *vlp = values[MA_VLP];
10529 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10530 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10532 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10536 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10538 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10539 int num_maps = g_list_length(vmls);
10542 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10546 // Convert from list of vmls to list of names. Allowing the user to select one of them
10547 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10548 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10550 gchar **np = map_names;
10551 VikMapsLayer **lp = map_layers;
10553 for (i = 0; i < num_maps; i++) {
10554 vml = (VikMapsLayer *)(vmls->data);
10556 *np++ = vik_maps_layer_get_map_label(vml);
10559 // Mark end of the array lists
10563 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10564 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10565 if (cur_zoom == zoom_vals[default_zoom])
10568 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10570 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10573 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10576 for (i = 0; i < num_maps; i++)
10577 g_free(map_names[i]);
10579 g_free(map_layers);
10585 /**** lowest waypoint number calculation ***/
10586 static gint highest_wp_number_name_to_number(const gchar *name) {
10587 if ( strlen(name) == 3 ) {
10588 int n = atoi(name);
10589 if ( n < 100 && name[0] != '0' )
10591 if ( n < 10 && name[0] != '0' )
10599 static void highest_wp_number_reset(VikTrwLayer *vtl)
10601 vtl->highest_wp_number = -1;
10604 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10606 /* if is bigger that top, add it */
10607 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10608 if ( new_wp_num > vtl->highest_wp_number )
10609 vtl->highest_wp_number = new_wp_num;
10612 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10614 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10615 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10616 if ( vtl->highest_wp_number == old_wp_num ) {
10618 vtl->highest_wp_number--;
10620 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10621 /* search down until we find something that *does* exist */
10623 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10624 vtl->highest_wp_number--;
10625 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10630 /* get lowest unused number */
10631 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10634 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10636 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10637 return g_strdup(buf);
10641 * trw_layer_create_track_list_both:
10643 * Create the latest list of tracks and routes
10645 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10647 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10648 GList *tracks = NULL;
10649 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10650 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10652 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10655 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10657 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10659 gchar *title = NULL;
10660 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10661 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10663 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10665 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10669 static void trw_layer_track_list_dialog ( menu_array_layer values )
10671 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10673 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10674 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10678 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10680 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10682 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10683 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );