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 GType vik_trw_layer_get_type ()
776 static GType vtl_type = 0;
780 static const GTypeInfo vtl_info =
782 sizeof (VikTrwLayerClass),
783 NULL, /* base_init */
784 NULL, /* base_finalize */
785 NULL, /* class init */
786 NULL, /* class_finalize */
787 NULL, /* class_data */
788 sizeof (VikTrwLayer),
790 NULL /* instance init */
792 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
798 VikTRWMetadata *vik_trw_metadata_new()
800 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
803 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
808 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
810 return vtl->metadata;
813 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
816 vik_trw_metadata_free ( vtl->metadata );
817 vtl->metadata = metadata;
822 const gchar *date_str;
824 const VikWaypoint *wpt;
829 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
833 // Might be an easier way to compare dates rather than converting the strings all the time...
834 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
835 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
837 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
846 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
850 // Might be an easier way to compare dates rather than converting the strings all the time...
851 if ( wpt->has_timestamp ) {
852 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
854 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
864 * Find an item by date
866 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
870 df.date_str = date_str;
875 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
877 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
879 if ( select && df.found ) {
880 if ( do_tracks && df.trk ) {
881 struct LatLon maxmin[2] = { {0,0}, {0,0} };
882 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
883 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
884 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
887 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord) );
888 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
890 vik_layer_emit_update ( VIK_LAYER(vtl) );
895 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
897 static menu_array_sublayer values;
903 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
906 values[MA_VTL] = vtl;
907 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
908 values[MA_SUBLAYER_ID] = sublayer;
909 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
911 trw_layer_delete_item ( values );
914 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
916 static menu_array_sublayer values;
922 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
925 values[MA_VTL] = vtl;
926 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
927 values[MA_SUBLAYER_ID] = sublayer;
928 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
930 trw_layer_copy_item_cb(values);
931 trw_layer_cut_item_cb(values);
934 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
936 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
937 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
938 gpointer * sublayer = values[MA_SUBLAYER_ID];
942 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
946 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
947 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
948 if ( wp && wp->name )
951 name = NULL; // Broken :(
953 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
954 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
955 if ( trk && trk->name )
958 name = NULL; // Broken :(
961 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
962 if ( trk && trk->name )
965 name = NULL; // Broken :(
968 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
969 subtype, len, name, data);
973 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
975 trw_layer_copy_item_cb(values);
976 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
977 trw_layer_delete_item(values);
980 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
982 // Slightly cheating method, routing via the panels capability
983 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
986 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
996 GByteArray *ba = g_byte_array_new ();
998 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
999 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1000 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1001 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1003 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1006 g_byte_array_append ( ba, id, il );
1014 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1021 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1025 w = vik_waypoint_unmarshall ( item, len );
1026 // When copying - we'll create a new name based on the original
1027 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1028 vik_trw_layer_add_waypoint ( vtl, name, w );
1029 waypoint_convert (NULL, w, &vtl->coord_mode);
1032 trw_layer_calculate_bounds_waypoints ( vtl );
1034 // Consider if redraw necessary for the new item
1035 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1036 vik_layer_emit_update ( VIK_LAYER(vtl) );
1039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1043 t = vik_track_unmarshall ( item, len );
1044 // When copying - we'll create a new name based on the original
1045 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1046 vik_trw_layer_add_track ( vtl, name, t );
1047 vik_track_convert (t, vtl->coord_mode);
1050 // Consider if redraw necessary for the new item
1051 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1052 vik_layer_emit_update ( VIK_LAYER(vtl) );
1055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1059 t = vik_track_unmarshall ( item, len );
1060 // When copying - we'll create a new name based on the original
1061 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1062 vik_trw_layer_add_route ( vtl, name, t );
1063 vik_track_convert (t, vtl->coord_mode);
1066 // Consider if redraw necessary for the new item
1067 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1068 vik_layer_emit_update ( VIK_LAYER(vtl) );
1074 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1081 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1085 case PARAM_TV: vtl->tracks_visible = data.b; break;
1086 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1087 case PARAM_RV: vtl->routes_visible = data.b; break;
1088 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1089 case PARAM_TLFONTSIZE:
1090 if ( data.u < FS_NUM_SIZES ) {
1091 vtl->track_font_size = data.u;
1092 g_free ( vtl->track_fsize_str );
1093 switch ( vtl->track_font_size ) {
1094 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1095 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1096 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1097 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1098 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1099 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1100 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1104 case PARAM_DM: vtl->drawmode = data.u; break;
1106 vtl->track_color = data.c;
1107 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1109 case PARAM_DP: vtl->drawpoints = data.b; break;
1111 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1112 vtl->drawpoints_size = data.u;
1114 case PARAM_DE: vtl->drawelevation = data.b; break;
1115 case PARAM_DS: vtl->drawstops = data.b; break;
1116 case PARAM_DL: vtl->drawlines = data.b; break;
1117 case PARAM_DD: vtl->drawdirections = data.b; break;
1119 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1120 vtl->drawdirections_size = data.u;
1122 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1123 vtl->stop_length = data.u;
1125 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1126 vtl->elevation_factor = data.u;
1128 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1130 vtl->line_thickness = data.u;
1131 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1134 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1136 vtl->bg_line_thickness = data.u;
1137 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1141 vtl->track_bg_color = data.c;
1142 if ( vtl->track_bg_gc )
1143 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1145 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1146 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1147 case PARAM_DLA: vtl->drawlabels = data.b; break;
1148 case PARAM_DI: vtl->drawimages = data.b; break;
1149 case PARAM_IS: if ( data.u != vtl->image_size )
1151 vtl->image_size = data.u;
1152 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1153 g_queue_free ( vtl->image_cache );
1154 vtl->image_cache = g_queue_new ();
1157 case PARAM_IA: vtl->image_alpha = data.u; break;
1158 case PARAM_ICS: vtl->image_cache_size = data.u;
1159 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1160 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1163 vtl->waypoint_color = data.c;
1164 if ( vtl->waypoint_gc )
1165 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1168 vtl->waypoint_text_color = data.c;
1169 if ( vtl->waypoint_text_gc )
1170 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1173 vtl->waypoint_bg_color = data.c;
1174 if ( vtl->waypoint_bg_gc )
1175 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1178 vtl->wpbgand = data.b;
1179 if ( vtl->waypoint_bg_gc )
1180 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1182 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1183 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1184 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1185 case PARAM_WPFONTSIZE:
1186 if ( data.u < FS_NUM_SIZES ) {
1187 vtl->wp_font_size = data.u;
1188 g_free ( vtl->wp_fsize_str );
1189 switch ( vtl->wp_font_size ) {
1190 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1191 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1192 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1193 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1194 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1195 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1196 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1200 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1202 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1203 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1204 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1205 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1211 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1213 VikLayerParamData rv;
1216 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1217 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1218 case PARAM_RV: rv.b = vtl->routes_visible; break;
1219 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1220 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1221 case PARAM_DM: rv.u = vtl->drawmode; break;
1222 case PARAM_TC: rv.c = vtl->track_color; break;
1223 case PARAM_DP: rv.b = vtl->drawpoints; break;
1224 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1225 case PARAM_DE: rv.b = vtl->drawelevation; break;
1226 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1227 case PARAM_DS: rv.b = vtl->drawstops; break;
1228 case PARAM_SL: rv.u = vtl->stop_length; break;
1229 case PARAM_DL: rv.b = vtl->drawlines; break;
1230 case PARAM_DD: rv.b = vtl->drawdirections; break;
1231 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1232 case PARAM_LT: rv.u = vtl->line_thickness; break;
1233 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1234 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1235 case PARAM_DI: rv.b = vtl->drawimages; break;
1236 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1237 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1238 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1239 case PARAM_IS: rv.u = vtl->image_size; break;
1240 case PARAM_IA: rv.u = vtl->image_alpha; break;
1241 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1242 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1243 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1244 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1245 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1246 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1247 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1248 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1249 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1250 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1252 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1253 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1254 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1255 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1261 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1263 // This '-3' is to account for the first few parameters not in the properties
1264 const gint OFFSET = -3;
1266 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1267 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1270 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1271 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1272 GtkWidget **ww2 = values[UI_CHG_LABELS];
1273 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1274 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1275 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1276 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1277 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1278 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1279 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1280 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1281 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1282 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1283 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1284 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1287 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1290 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1291 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1292 GtkWidget **ww2 = values[UI_CHG_LABELS];
1293 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1294 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1295 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1296 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1297 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1298 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1299 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1300 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1301 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1302 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1303 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1304 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1305 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1306 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1307 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1308 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1311 // Alter sensitivity of all track colours according to the draw track mode.
1314 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1315 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1316 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1317 GtkWidget **ww2 = values[UI_CHG_LABELS];
1318 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1319 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1320 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1321 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1324 case PARAM_MDTIME: {
1325 // Force metadata->timestamp to be always read-only for now.
1326 GtkWidget **ww = values[UI_CHG_WIDGETS];
1327 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1328 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1330 // NB Since other track settings have been split across tabs,
1331 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1336 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1343 // Use byte arrays to store sublayer data
1344 // much like done elsewhere e.g. vik_layer_marshall_params()
1345 GByteArray *ba = g_byte_array_new ( );
1350 guint object_length;
1353 // the length of the item
1354 // the sublayer type of item
1355 // the the actual item
1356 #define tlm_append(object_pointer, size, type) \
1358 object_length = (size); \
1359 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1360 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1361 g_byte_array_append ( ba, (object_pointer), object_length );
1363 // Layer parameters first
1364 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1365 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1366 g_byte_array_append ( ba, pd, pl );
1369 // Now sublayer data
1370 GHashTableIter iter;
1371 gpointer key, value;
1374 g_hash_table_iter_init ( &iter, vtl->waypoints );
1375 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1376 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1377 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1382 g_hash_table_iter_init ( &iter, vtl->tracks );
1383 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1384 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1385 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1390 g_hash_table_iter_init ( &iter, vtl->routes );
1391 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1392 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1393 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1403 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1405 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1407 gint consumed_length;
1409 // First the overall layer parameters
1410 memcpy(&pl, data, sizeof(pl));
1412 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1415 consumed_length = pl;
1416 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1418 #define tlm_size (*(gint *)data)
1419 // See marshalling above for order of how this is written
1421 data += sizeof_len_and_subtype + tlm_size;
1423 // Now the individual sublayers:
1425 while ( *data && consumed_length < len ) {
1426 // Normally four extra bytes at the end of the datastream
1427 // (since it's a GByteArray and that's where it's length is stored)
1428 // So only attempt read when there's an actual block of sublayer data
1429 if ( consumed_length + tlm_size < len ) {
1431 // Reuse pl to read the subtype from the data stream
1432 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1434 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1435 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1436 gchar *name = g_strdup ( trk->name );
1437 vik_trw_layer_add_track ( vtl, name, trk );
1440 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1441 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1442 gchar *name = g_strdup ( wp->name );
1443 vik_trw_layer_add_waypoint ( vtl, name, wp );
1446 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1447 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1448 gchar *name = g_strdup ( trk->name );
1449 vik_trw_layer_add_route ( vtl, name, trk );
1453 consumed_length += tlm_size + sizeof_len_and_subtype;
1456 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1458 // Not stored anywhere else so need to regenerate
1459 trw_layer_calculate_bounds_waypoints ( vtl );
1464 // Keep interesting hash function at least visible
1466 static guint strcase_hash(gconstpointer v)
1468 // 31 bit hash function
1471 gchar s[128]; // malloc is too slow for reading big files
1474 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1475 p[i] = toupper(t[i]);
1481 for (p += 1; *p != '\0'; p++)
1482 h = (h << 5) - h + *p;
1489 // Stick a 1 at the end of the function name to make it more unique
1490 // thus more easily searchable in a simple text editor
1491 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1493 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1494 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1496 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1497 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1499 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1500 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1501 // and with normal PC processing capabilities - it has negligibile performance impact
1502 // This also minimized the amount of rework - as the management of the hash tables already exists.
1504 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1505 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1506 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1508 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1509 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1510 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1511 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1512 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1513 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1515 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1517 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1519 // Param settings that are not available via the GUI
1520 // Force to on after processing params (which defaults them to off with a zero value)
1521 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1523 rv->metadata = vik_trw_metadata_new ();
1524 rv->draw_sync_done = TRUE;
1525 rv->draw_sync_do = TRUE;
1526 // Everything else is 0, FALSE or NULL
1532 static void trw_layer_free ( VikTrwLayer *trwlayer )
1534 g_hash_table_destroy(trwlayer->waypoints);
1535 g_hash_table_destroy(trwlayer->waypoints_iters);
1536 g_hash_table_destroy(trwlayer->tracks);
1537 g_hash_table_destroy(trwlayer->tracks_iters);
1538 g_hash_table_destroy(trwlayer->routes);
1539 g_hash_table_destroy(trwlayer->routes_iters);
1541 /* ODC: replace with GArray */
1542 trw_layer_free_track_gcs ( trwlayer );
1544 if ( trwlayer->wp_right_click_menu )
1545 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1547 if ( trwlayer->track_right_click_menu )
1548 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1550 if ( trwlayer->tracklabellayout != NULL)
1551 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1553 if ( trwlayer->wplabellayout != NULL)
1554 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1556 if ( trwlayer->waypoint_gc != NULL )
1557 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1559 if ( trwlayer->waypoint_text_gc != NULL )
1560 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1562 if ( trwlayer->waypoint_bg_gc != NULL )
1563 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1565 g_free ( trwlayer->wp_fsize_str );
1566 g_free ( trwlayer->track_fsize_str );
1568 if ( trwlayer->tpwin != NULL )
1569 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1571 if ( trwlayer->tracks_analysis_dialog != NULL )
1572 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1574 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1575 g_queue_free ( trwlayer->image_cache );
1578 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1582 dp->highlight = highlight;
1583 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1584 dp->xmpp = vik_viewport_get_xmpp ( vp );
1585 dp->ympp = vik_viewport_get_ympp ( vp );
1586 dp->width = vik_viewport_get_width ( vp );
1587 dp->height = vik_viewport_get_height ( vp );
1588 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1589 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1591 dp->center = vik_viewport_get_center ( vp );
1592 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1593 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1598 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1599 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1600 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1602 dp->ce1 = dp->center->east_west-w2;
1603 dp->ce2 = dp->center->east_west+w2;
1604 dp->cn1 = dp->center->north_south-h2;
1605 dp->cn2 = dp->center->north_south+h2;
1606 } else if ( dp->lat_lon ) {
1607 VikCoord upperleft, bottomright;
1608 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1609 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1610 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1611 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1612 dp->ce1 = upperleft.east_west;
1613 dp->ce2 = bottomright.east_west;
1614 dp->cn1 = bottomright.north_south;
1615 dp->cn2 = upperleft.north_south;
1618 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1622 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1623 * Here a simple traffic like light colour system is used:
1624 * . slow points are red
1625 * . average is yellow
1626 * . fast points are green
1628 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1631 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1632 if ( average_speed > 0 ) {
1633 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1634 if ( rv < low_speed )
1635 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1636 else if ( rv > high_speed )
1637 return VIK_TRW_LAYER_TRACK_GC_FAST;
1639 return VIK_TRW_LAYER_TRACK_GC_AVER;
1642 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1645 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1647 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1648 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1649 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1650 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1654 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1656 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1658 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1659 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1661 // Fallback if parse failure
1662 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1664 g_free ( label_markup );
1666 gint label_x, label_y;
1668 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1670 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1671 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1675 * distance_in_preferred_units:
1676 * @dist: The source distance in standard SI Units (i.e. metres)
1678 * TODO: This is a generic function that could be moved into globals.c or utils.c
1680 * Probably best used if you have a only few conversions to perform.
1681 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1682 * since it will be doing the preference check on each call
1684 * Returns: The distance in the units as specified by the preferences
1686 static gdouble distance_in_preferred_units ( gdouble dist )
1689 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1690 switch (dist_units) {
1691 case VIK_UNITS_DISTANCE_MILES:
1692 mydist = VIK_METERS_TO_MILES(dist);
1694 // VIK_UNITS_DISTANCE_KILOMETRES:
1696 mydist = dist/1000.0;
1703 * trw_layer_draw_dist_labels:
1705 * Draw a few labels along a track at nicely seperated distances
1706 * This might slow things down if there's many tracks being displayed with this on.
1708 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1710 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1711 25.0, 40.0, 50.0, 75.0, 100.0,
1712 150.0, 200.0, 250.0, 500.0, 1000.0};
1714 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1716 // Convert to specified unit to find the friendly breakdown value
1717 dist = distance_in_preferred_units ( dist );
1721 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1722 if ( chunksd[i] > dist ) {
1724 dist = chunksd[index];
1729 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1731 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1732 gdouble dist_i = dist * i;
1734 // Convert distance back into metres for use in finding a trackpoint
1735 switch (dist_units) {
1736 case VIK_UNITS_DISTANCE_MILES:
1737 dist_i = VIK_MILES_TO_METERS(dist_i);
1739 // VIK_UNITS_DISTANCE_KILOMETRES:
1741 dist_i = dist_i*1000.0;
1745 gdouble dist_current = 0.0;
1746 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1747 gdouble dist_next = 0.0;
1748 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1750 gdouble dist_between_tps = fabs (dist_next - dist_current);
1751 gdouble ratio = 0.0;
1752 // Prevent division by 0 errors
1753 if ( dist_between_tps > 0.0 )
1754 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1756 if ( tp_current && tp_next ) {
1757 // Construct the name based on the distance value
1760 switch (dist_units) {
1761 case VIK_UNITS_DISTANCE_MILES:
1762 units = g_strdup ( _("miles") );
1764 // VIK_UNITS_DISTANCE_KILOMETRES:
1766 units = g_strdup ( _("km") );
1770 // Convert for display
1771 dist_i = distance_in_preferred_units ( dist_i );
1773 // Make the precision of the output related to the unit size.
1775 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1776 else if ( index == 1 )
1777 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1779 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1782 struct LatLon ll_current, ll_next;
1783 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1784 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1786 // positional interpolation
1787 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1788 // but should be good enough over the small scale that I anticipate usage on
1789 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1790 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1792 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1795 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1796 fgcolour = gdk_color_to_string ( &(trk->color) );
1798 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1800 // if highlight mode on, then colour the background in the highlight colour
1802 if ( drawing_highlight )
1803 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1805 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1807 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1809 g_free ( fgcolour );
1810 g_free ( bgcolour );
1817 * trw_layer_draw_track_name_labels:
1819 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1821 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1824 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1825 fgcolour = gdk_color_to_string ( &(trk->color) );
1827 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1829 // if highlight mode on, then colour the background in the highlight colour
1831 if ( drawing_highlight )
1832 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1834 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1836 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1838 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1839 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1840 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1841 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1842 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1843 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1845 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1847 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1850 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1851 // No other labels to draw
1854 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1857 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1860 VikCoord begin_coord = tp_begin->coord;
1861 VikCoord end_coord = tp_end->coord;
1863 gboolean done_start_end = FALSE;
1865 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1866 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1868 // This number can be configured via the settings if you really want to change it
1869 gdouble distance_diff;
1870 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1871 distance_diff = 100.0; // Metres
1873 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1874 // Start and end 'close' together so only draw one label at an average location
1875 gint x1, x2, y1, y2;
1876 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1877 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1879 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1881 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1882 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1885 done_start_end = TRUE;
1889 if ( ! done_start_end ) {
1890 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1891 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1892 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1893 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1894 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1895 g_free ( name_start );
1897 // Don't draw end label if this is the one being created
1898 if ( trk != dp->vtl->current_track ) {
1899 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1900 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1901 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1902 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1903 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1904 g_free ( name_end );
1909 g_free ( fgcolour );
1910 g_free ( bgcolour );
1914 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1916 if ( ! track->visible )
1919 /* TODO: this function is a mess, get rid of any redundancy */
1920 GList *list = track->trackpoints;
1922 gboolean useoldvals = TRUE;
1924 gboolean drawpoints;
1926 gboolean drawelevation;
1927 gdouble min_alt, max_alt, alt_diff = 0;
1929 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1930 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1933 if ( dp->vtl->drawelevation )
1935 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1936 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1937 alt_diff = max_alt - min_alt;
1940 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1941 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1942 trw_layer_draw_track ( id, track, dp, TRUE );
1944 if ( draw_track_outline )
1945 drawpoints = drawstops = FALSE;
1947 drawpoints = dp->vtl->drawpoints;
1948 drawstops = dp->vtl->drawstops;
1951 gboolean drawing_highlight = FALSE;
1952 /* Current track - used for creation */
1953 if ( track == dp->vtl->current_track )
1954 main_gc = dp->vtl->current_track_gc;
1956 if ( dp->highlight ) {
1957 /* Draw all tracks of the layer in special colour
1958 NB this supercedes the drawmode */
1959 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1960 drawing_highlight = TRUE;
1962 if ( !drawing_highlight ) {
1963 // Still need to figure out the gc according to the drawing mode:
1964 switch ( dp->vtl->drawmode ) {
1965 case DRAWMODE_BY_TRACK:
1966 if ( dp->vtl->track_1color_gc )
1967 g_object_unref ( dp->vtl->track_1color_gc );
1968 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1969 main_gc = dp->vtl->track_1color_gc;
1972 // Mostly for DRAWMODE_ALL_SAME_COLOR
1973 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1974 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1981 int x, y, oldx, oldy;
1982 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1984 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1986 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1988 // Draw the first point as something a bit different from the normal points
1989 // ATM it's slightly bigger and a triangle
1991 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1992 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1998 gdouble average_speed = 0.0;
1999 gdouble low_speed = 0.0;
2000 gdouble high_speed = 0.0;
2001 // If necessary calculate these values - which is done only once per track redraw
2002 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2003 // the percentage factor away from the average speed determines transistions between the levels
2004 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2005 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2006 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2009 while ((list = g_list_next(list)))
2011 tp = VIK_TRACKPOINT(list->data);
2012 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2014 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2015 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2016 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2017 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2018 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2020 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2023 * If points are the same in display coordinates, don't draw.
2025 if ( useoldvals && x == oldx && y == oldy )
2027 // Still need to process points to ensure 'stops' are drawn if required
2028 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2029 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2030 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 );
2035 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2036 if ( drawpoints || dp->vtl->drawlines ) {
2037 // setup main_gc for both point and line drawing
2038 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2039 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 ) );
2043 if ( drawpoints && ! draw_track_outline )
2048 * The concept of drawing stops is that a trackpoint
2049 * that is if the next trackpoint has a timestamp far into
2050 * the future, we draw a circle of 6x trackpoint size,
2051 * instead of a rectangle of 2x trackpoint size.
2052 * This is drawn first so the trackpoint will be drawn on top
2055 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2056 /* Stop point. Draw 6x circle. Always in redish colour */
2057 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 );
2059 /* Regular point - draw 2x square. */
2060 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2063 /* Final point - draw 4x circle. */
2064 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 );
2067 if ((!tp->newsegment) && (dp->vtl->drawlines))
2070 /* UTM only: zone check */
2071 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2072 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2075 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2077 if ( draw_track_outline ) {
2078 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2082 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2084 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2086 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2091 tmp[1].y = oldy-FIXALTITUDE(list->data);
2093 tmp[2].y = y-FIXALTITUDE(list->next->data);
2098 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2099 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2101 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2102 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2104 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2109 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2110 // Draw an arrow at the mid point to show the direction of the track
2111 // Code is a rework from vikwindow::draw_ruler()
2112 gint midx = (oldx + x) / 2;
2113 gint midy = (oldy + y) / 2;
2115 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2116 // Avoid divide by zero and ensure at least 1 pixel big
2118 gdouble dx = (oldx - midx) / len;
2119 gdouble dy = (oldy - midy) / len;
2120 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2121 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2131 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2133 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2134 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2136 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2138 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2139 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 ));
2143 * If points are the same in display coordinates, don't draw.
2145 if ( x != oldx || y != oldy )
2147 if ( draw_track_outline )
2148 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2150 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2156 * If points are the same in display coordinates, don't draw.
2158 if ( x != oldx && y != oldy )
2160 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2161 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2169 // Labels drawn after the trackpoints, so the labels are on top
2170 if ( dp->vtl->track_draw_labels ) {
2171 if ( track->max_number_dist_labels > 0 ) {
2172 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2175 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2176 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2182 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2184 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2185 trw_layer_draw_track ( id, track, dp, FALSE );
2189 static void cached_pixbuf_free ( CachedPixbuf *cp )
2191 g_object_unref ( G_OBJECT(cp->pixbuf) );
2192 g_free ( cp->image );
2195 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2197 return strcmp ( cp->image, name );
2200 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2203 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2204 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2205 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2208 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2210 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2212 if ( wp->image && dp->vtl->drawimages )
2214 GdkPixbuf *pixbuf = NULL;
2217 if ( dp->vtl->image_alpha == 0)
2220 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2222 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2225 gchar *image = wp->image;
2226 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2227 if ( ! regularthumb )
2229 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2230 image = "\x12\x00"; /* this shouldn't occur naturally. */
2234 CachedPixbuf *cp = NULL;
2235 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2236 if ( dp->vtl->image_size == 128 )
2237 cp->pixbuf = regularthumb;
2240 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2241 g_assert ( cp->pixbuf );
2242 g_object_unref ( G_OBJECT(regularthumb) );
2244 cp->image = g_strdup ( image );
2246 /* needed so 'click picture' tool knows how big the pic is; we don't
2247 * store it in cp because they may have been freed already. */
2248 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2249 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2251 g_queue_push_head ( dp->vtl->image_cache, cp );
2252 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2253 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2255 pixbuf = cp->pixbuf;
2259 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2265 w = gdk_pixbuf_get_width ( pixbuf );
2266 h = gdk_pixbuf_get_height ( pixbuf );
2268 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2270 if ( dp->highlight ) {
2271 // Highlighted - so draw a little border around the chosen one
2272 // single line seems a little weak so draw 2 of them
2273 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2274 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2275 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2276 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2279 if ( dp->vtl->image_alpha == 255 )
2280 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2282 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2284 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2288 // Draw appropriate symbol - either symbol image or simple types
2289 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2290 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 );
2292 else if ( wp == dp->vtl->current_wp ) {
2293 switch ( dp->vtl->wp_symbol ) {
2294 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;
2295 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;
2296 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;
2297 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 );
2298 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 );
2302 switch ( dp->vtl->wp_symbol ) {
2303 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;
2304 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;
2305 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;
2306 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 );
2307 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;
2311 if ( dp->vtl->drawlabels )
2313 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2314 gint label_x, label_y;
2316 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2318 // Could this stored in the waypoint rather than recreating each pass?
2319 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2321 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2322 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2324 // Fallback if parse failure
2325 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2327 g_free ( wp_label_markup );
2329 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2330 label_x = x - width/2;
2331 if ( wp->symbol_pixbuf )
2332 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2334 label_y = y - dp->vtl->wp_size - height - 2;
2336 /* if highlight mode on, then draw background text in highlight colour */
2337 if ( dp->highlight )
2338 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2340 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2341 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2346 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2348 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2349 trw_layer_draw_waypoint ( id, wp, dp );
2353 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2355 static struct DrawingParams dp;
2356 g_assert ( l != NULL );
2358 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2360 if ( l->tracks_visible )
2361 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2363 if ( l->routes_visible )
2364 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2366 if (l->waypoints_visible)
2367 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2370 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2372 // TODO: learn which highlight vtl in dp somehow or just ask vikwindow here,
2373 // then check if highlighted vtl and skip drawing if (highlight mode off)
2374 // as it will then get redrawn
2375 // may seem slightly inefficient - but for very large vtls, this should save redraws...
2376 trw_layer_draw_with_highlight ( l, data, FALSE ) ;
2379 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2381 // Check the layer for visibility (including all the parents visibilities)
2382 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2384 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2388 * vik_trw_layer_draw_highlight_item:
2390 * Only handles a single track or waypoint ATM
2391 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2393 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2395 // Check the layer for visibility (including all the parents visibilities)
2396 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2399 static struct DrawingParams dp;
2400 init_drawing_params ( &dp, vtl, vvp, TRUE );
2403 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2405 trw_layer_draw_track_cb ( NULL, trk, &dp );
2407 if ( vtl->waypoints_visible && wpt ) {
2408 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2413 * vik_trw_layer_draw_highlight_item:
2415 * Generally for drawing all tracks or routes or waypoints
2416 * trks may be actually routes
2417 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2419 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, 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)) )
2425 static struct DrawingParams dp;
2426 init_drawing_params ( &dp, vtl, vvp, TRUE );
2429 gboolean is_routes = (trks == vtl->routes);
2430 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2432 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2435 if ( vtl->waypoints_visible && wpts )
2436 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2439 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2442 if ( vtl->track_bg_gc )
2444 g_object_unref ( vtl->track_bg_gc );
2445 vtl->track_bg_gc = NULL;
2447 if ( vtl->track_1color_gc )
2449 g_object_unref ( vtl->track_1color_gc );
2450 vtl->track_1color_gc = NULL;
2452 if ( vtl->current_track_gc )
2454 g_object_unref ( vtl->current_track_gc );
2455 vtl->current_track_gc = NULL;
2457 if ( vtl->current_track_newpoint_gc )
2459 g_object_unref ( vtl->current_track_newpoint_gc );
2460 vtl->current_track_newpoint_gc = NULL;
2463 if ( ! vtl->track_gc )
2465 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2466 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2467 g_array_free ( vtl->track_gc, TRUE );
2468 vtl->track_gc = NULL;
2471 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2473 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2474 gint width = vtl->line_thickness;
2476 if ( vtl->track_gc )
2477 trw_layer_free_track_gcs ( vtl );
2479 if ( vtl->track_bg_gc )
2480 g_object_unref ( vtl->track_bg_gc );
2481 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2483 // Ensure new track drawing heeds line thickness setting
2484 // however always have a minium of 2, as 1 pixel is really narrow
2485 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2487 if ( vtl->current_track_gc )
2488 g_object_unref ( vtl->current_track_gc );
2489 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2490 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2492 // 'newpoint' gc is exactly the same as the current track gc
2493 if ( vtl->current_track_newpoint_gc )
2494 g_object_unref ( vtl->current_track_newpoint_gc );
2495 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2496 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2498 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2500 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2501 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2503 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2504 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2505 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2507 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2509 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2512 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2514 VikTrwLayer *rv = trw_layer_new1 ( vp );
2515 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2517 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2518 /* early exit, as the rest is GUI related */
2522 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2523 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2525 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2526 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2528 trw_layer_new_track_gcs ( rv, vp );
2530 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2531 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2532 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2533 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2535 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2537 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2542 #define SMALL_ICON_SIZE 18
2544 * Can accept a null symbol, and may return null value
2546 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2548 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2549 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2550 // So needing a small icon for the treeview may need some resizing:
2551 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2552 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2556 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2558 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2560 GdkPixbuf *pixbuf = NULL;
2562 if ( track->has_color ) {
2563 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2564 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2565 // Here is some magic found to do the conversion
2566 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2567 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2568 ((track->color.green & 0xff00) << 8) |
2569 (track->color.blue & 0xff00);
2571 gdk_pixbuf_fill ( pixbuf, pixel );
2574 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 );
2577 g_object_unref (pixbuf);
2579 *new_iter = *((GtkTreeIter *) pass_along[1]);
2580 if ( track->is_route )
2581 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2583 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2585 if ( ! track->visible )
2586 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2589 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2591 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2593 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 );
2595 *new_iter = *((GtkTreeIter *) pass_along[1]);
2596 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2598 if ( ! wp->visible )
2599 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2602 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2604 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2607 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2609 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2612 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2614 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2617 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2620 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2622 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2623 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2625 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2627 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2630 if ( g_hash_table_size (vtl->routes) > 0 ) {
2631 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2633 pass_along[0] = &(vtl->routes_iter);
2634 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2636 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2638 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2641 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2642 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2644 pass_along[0] = &(vtl->waypoints_iter);
2645 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2647 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2649 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2654 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2658 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2659 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2660 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2661 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2663 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2665 return (t->visible ^= 1);
2669 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2671 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2673 return (t->visible ^= 1);
2677 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2679 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2681 return (t->visible ^= 1);
2690 * Return a property about tracks for this layer
2692 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2694 return vtl->line_thickness;
2697 // Structure to hold multiple track information for a layer
2706 * Build up layer multiple track information via updating the tooltip_tracks structure
2708 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2710 tt->length = tt->length + vik_track_get_length (tr);
2712 // Ensure times are available
2713 if ( tr->trackpoints &&
2714 vik_track_get_tp_first(tr)->has_timestamp &&
2715 vik_track_get_tp_last(tr)->has_timestamp ) {
2718 t1 = vik_track_get_tp_first(tr)->timestamp;
2719 t2 = vik_track_get_tp_last(tr)->timestamp;
2721 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2722 // Hence initialize to the first 'proper' value
2723 if ( tt->start_time == 0 )
2724 tt->start_time = t1;
2725 if ( tt->end_time == 0 )
2728 // Update find the earliest / last times
2729 if ( t1 < tt->start_time )
2730 tt->start_time = t1;
2731 if ( t2 > tt->end_time )
2734 // Keep track of total time
2735 // there maybe gaps within a track (eg segments)
2736 // but this should be generally good enough for a simple indicator
2737 tt->duration = tt->duration + (int)(t2-t1);
2742 * Generate tooltip text for the layer.
2743 * This is relatively complicated as it considers information for
2744 * no tracks, a single track or multiple tracks
2745 * (which may or may not have timing information)
2747 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2758 static gchar tmp_buf[128];
2761 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2763 // Safety check - I think these should always be valid
2764 if ( vtl->tracks && vtl->waypoints ) {
2765 tooltip_tracks tt = { 0.0, 0, 0 };
2766 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2768 GDate* gdate_start = g_date_new ();
2769 g_date_set_time_t (gdate_start, tt.start_time);
2771 GDate* gdate_end = g_date_new ();
2772 g_date_set_time_t (gdate_end, tt.end_time);
2774 if ( g_date_compare (gdate_start, gdate_end) ) {
2775 // Dates differ so print range on separate line
2776 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2777 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2778 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2781 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2782 if ( tt.start_time != 0 )
2783 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2787 if ( tt.length > 0.0 ) {
2788 gdouble len_in_units;
2790 // Setup info dependent on distance units
2791 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2792 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2793 len_in_units = VIK_METERS_TO_MILES(tt.length);
2796 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2797 len_in_units = tt.length/1000.0;
2800 // Timing information if available
2802 if ( tt.duration > 0 ) {
2803 g_snprintf (tbuf1, sizeof(tbuf1),
2804 _(" in %d:%02d hrs:mins"),
2805 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2807 g_snprintf (tbuf2, sizeof(tbuf2),
2808 _("\n%sTotal Length %.1f %s%s"),
2809 tbuf3, len_in_units, tbuf4, tbuf1);
2812 // Put together all the elements to form compact tooltip text
2813 g_snprintf (tmp_buf, sizeof(tmp_buf),
2814 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2815 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2817 g_date_free (gdate_start);
2818 g_date_free (gdate_end);
2825 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2829 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2831 // Very simple tooltip - may expand detail in the future...
2832 static gchar tmp_buf[32];
2833 g_snprintf (tmp_buf, sizeof(tmp_buf),
2835 g_hash_table_size (l->tracks));
2839 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2841 // Very simple tooltip - may expand detail in the future...
2842 static gchar tmp_buf[32];
2843 g_snprintf (tmp_buf, sizeof(tmp_buf),
2845 g_hash_table_size (l->routes));
2850 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2851 // Same tooltip for a route
2852 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2855 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2856 tr = g_hash_table_lookup ( l->tracks, sublayer );
2858 tr = g_hash_table_lookup ( l->routes, sublayer );
2861 // Could be a better way of handling strings - but this works...
2862 gchar time_buf1[20];
2863 gchar time_buf2[20];
2864 time_buf1[0] = '\0';
2865 time_buf2[0] = '\0';
2866 static gchar tmp_buf[100];
2867 // Compact info: Short date eg (11/20/99), duration and length
2868 // Hopefully these are the things that are most useful and so promoted into the tooltip
2869 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2870 // %x The preferred date representation for the current locale without the time.
2871 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2872 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2873 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2875 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2878 // Get length and consider the appropriate distance units
2879 gdouble tr_len = vik_track_get_length(tr);
2880 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2881 switch (dist_units) {
2882 case VIK_UNITS_DISTANCE_KILOMETRES:
2883 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2885 case VIK_UNITS_DISTANCE_MILES:
2886 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2895 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2897 // Very simple tooltip - may expand detail in the future...
2898 static gchar tmp_buf[32];
2899 g_snprintf (tmp_buf, sizeof(tmp_buf),
2901 g_hash_table_size (l->waypoints));
2905 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2907 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2908 // NB It's OK to return NULL
2913 return w->description;
2922 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2925 * set_statusbar_msg_info_trkpt:
2927 * Function to show track point information on the statusbar
2928 * Items displayed is controlled by the settings format code
2930 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2932 gchar *statusbar_format_code = NULL;
2933 gboolean need2free = FALSE;
2934 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2935 // Otherwise use default
2936 statusbar_format_code = g_strdup ( "KEATDN" );
2940 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2941 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2945 g_free ( statusbar_format_code );
2949 * Function to show basic waypoint information on the statusbar
2951 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2954 switch (a_vik_get_units_height ()) {
2955 case VIK_UNITS_HEIGHT_FEET:
2956 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2959 //VIK_UNITS_HEIGHT_METRES:
2960 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2964 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2965 // one can easily use the current pointer position to see this if needed
2966 gchar *lat = NULL, *lon = NULL;
2967 static struct LatLon ll;
2968 vik_coord_to_latlon (&(wpt->coord), &ll);
2969 a_coords_latlon_to_string ( &ll, &lat, &lon );
2971 // Combine parts to make overall message
2974 // Add comment if available
2975 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2977 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2978 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2985 * General layer selection function, find out which bit is selected and take appropriate action
2987 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2990 l->current_wp = NULL;
2991 l->current_wp_id = NULL;
2992 trw_layer_cancel_current_tp ( l, FALSE );
2995 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2999 case VIK_TREEVIEW_TYPE_LAYER:
3001 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3002 /* Mark for redraw */
3007 case VIK_TREEVIEW_TYPE_SUBLAYER:
3011 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3013 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3014 /* Mark for redraw */
3018 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3020 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3021 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3022 /* Mark for redraw */
3026 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3028 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3029 /* Mark for redraw */
3033 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3035 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3036 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3037 /* Mark for redraw */
3041 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3043 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3044 /* Mark for redraw */
3048 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3050 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3052 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3053 // Show some waypoint info
3054 set_statusbar_msg_info_wpt ( l, wpt );
3055 /* Mark for redraw */
3062 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3071 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3076 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3081 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3086 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3088 return l->waypoints;
3091 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3093 return vtl->tracks_iters;
3096 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3098 return vtl->routes_iters;
3101 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3103 return vtl->waypoints;
3106 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3108 return ! ( g_hash_table_size ( vtl->tracks ) ||
3109 g_hash_table_size ( vtl->routes ) ||
3110 g_hash_table_size ( vtl->waypoints ) );
3113 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3115 return vtl->tracks_visible;
3118 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3120 return vtl->routes_visible;
3123 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3125 return vtl->waypoints_visible;
3129 * ATM use a case sensitive find
3130 * Finds the first one
3132 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3134 if ( wp && wp->name )
3135 if ( ! strcmp ( wp->name, name ) )
3141 * Get waypoint by name - not guaranteed to be unique
3142 * Finds the first one
3144 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3146 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3150 * ATM use a case sensitive find
3151 * Finds the first one
3153 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3155 if ( trk && trk->name )
3156 if ( ! strcmp ( trk->name, name ) )
3162 * Get track by name - not guaranteed to be unique
3163 * Finds the first one
3165 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3167 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3171 * Get route by name - not guaranteed to be unique
3172 * Finds the first one
3174 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3176 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3179 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3181 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3182 maxmin[0].lat = trk->bbox.north;
3183 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3184 maxmin[1].lat = trk->bbox.south;
3185 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3186 maxmin[0].lon = trk->bbox.east;
3187 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3188 maxmin[1].lon = trk->bbox.west;
3191 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3193 // Continually reuse maxmin to find the latest maximum and minimum values
3194 // First set to waypoints bounds
3195 maxmin[0].lat = vtl->waypoints_bbox.north;
3196 maxmin[1].lat = vtl->waypoints_bbox.south;
3197 maxmin[0].lon = vtl->waypoints_bbox.east;
3198 maxmin[1].lon = vtl->waypoints_bbox.west;
3199 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3200 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3203 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3205 /* 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... */
3206 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3207 trw_layer_find_maxmin (vtl, maxmin);
3208 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3212 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3213 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3218 static void trw_layer_centerize ( menu_array_layer values )
3220 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3222 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3223 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3225 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3228 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3230 /* First set the center [in case previously viewing from elsewhere] */
3231 /* Then loop through zoom levels until provided positions are in view */
3232 /* This method is not particularly fast - but should work well enough */
3233 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3235 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3236 vik_viewport_set_center_coord ( vvp, &coord );
3238 /* Convert into definite 'smallest' and 'largest' positions */
3239 struct LatLon minmin;
3240 if ( maxmin[0].lat < maxmin[1].lat )
3241 minmin.lat = maxmin[0].lat;
3243 minmin.lat = maxmin[1].lat;
3245 struct LatLon maxmax;
3246 if ( maxmin[0].lon > maxmin[1].lon )
3247 maxmax.lon = maxmin[0].lon;
3249 maxmax.lon = maxmin[1].lon;
3251 /* Never zoom in too far - generally not that useful, as too close ! */
3252 /* Always recalculate the 'best' zoom level */
3254 vik_viewport_set_zoom ( vvp, zoom );
3256 gdouble min_lat, max_lat, min_lon, max_lon;
3257 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3258 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3259 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3260 /* NB I think the logic used in this test to determine if the bounds is within view
3261 fails if track goes across 180 degrees longitude.
3262 Hopefully that situation is not too common...
3263 Mind you viking doesn't really do edge locations to well anyway */
3264 if ( min_lat < minmin.lat &&
3265 max_lat > minmin.lat &&
3266 min_lon < maxmax.lon &&
3267 max_lon > maxmax.lon )
3268 /* Found within zoom level */
3273 vik_viewport_set_zoom ( vvp, zoom );
3277 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3279 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3280 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3281 trw_layer_find_maxmin (vtl, maxmin);
3282 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3285 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3290 static void trw_layer_auto_view ( menu_array_layer values )
3292 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3293 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3294 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3295 vik_layers_panel_emit_update ( vlp );
3298 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3301 static void trw_layer_export_gpspoint ( menu_array_layer values )
3303 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3305 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3307 g_free ( auto_save_name );
3310 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3312 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3314 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3316 g_free ( auto_save_name );
3319 static void trw_layer_export_gpx ( menu_array_layer values )
3321 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3323 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3325 g_free ( auto_save_name );
3328 static void trw_layer_export_kml ( menu_array_layer values )
3330 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3332 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3334 g_free ( auto_save_name );
3337 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3339 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3340 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3343 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3345 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3348 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3350 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3353 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3355 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3357 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3358 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3360 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3362 if ( !trk || !trk->name )
3365 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3367 gchar *label = NULL;
3368 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3369 label = _("Export Route as GPX");
3371 label = _("Export Track as GPX");
3372 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3374 g_free ( auto_save_name );
3377 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3379 wpu_udata *user_data = udata;
3380 if ( wp == user_data->wp ) {
3381 user_data->uuid = id;
3387 static void trw_layer_goto_wp ( menu_array_layer values )
3389 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3390 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3391 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3392 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3393 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3395 GTK_RESPONSE_REJECT,
3397 GTK_RESPONSE_ACCEPT,
3400 GtkWidget *label, *entry;
3401 label = gtk_label_new(_("Waypoint Name:"));
3402 entry = gtk_entry_new();
3404 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3405 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3406 gtk_widget_show_all ( label );
3407 gtk_widget_show_all ( entry );
3409 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3411 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3413 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3414 // Find *first* wp with the given name
3415 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3418 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3421 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3422 vik_layers_panel_emit_update ( vlp );
3424 // Find and select on the side panel
3429 // Hmmm, want key of it
3430 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3432 if ( wpf && udata.uuid ) {
3433 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3434 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3443 gtk_widget_destroy ( dia );
3446 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3448 gchar *default_name = highest_wp_number_get(vtl);
3449 VikWaypoint *wp = vik_waypoint_new();
3450 gchar *returned_name;
3452 wp->coord = *def_coord;
3454 // Attempt to auto set height if DEM data is available
3455 vik_waypoint_apply_dem_data ( wp, TRUE );
3457 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3459 if ( returned_name )
3462 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3463 g_free (default_name);
3464 g_free (returned_name);
3467 g_free (default_name);
3468 vik_waypoint_free(wp);
3472 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3474 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3475 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3476 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3477 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3478 VikViewport *vvp = vik_window_viewport(vw);
3480 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3481 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3482 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3483 trw_layer_calculate_bounds_waypoints ( vtl );
3484 vik_layers_panel_emit_update ( vlp );
3487 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3489 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3490 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3491 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3493 trw_layer_find_maxmin (vtl, maxmin);
3494 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3495 trw_layer_calculate_bounds_waypoints ( vtl );
3496 vik_layers_panel_emit_update ( vlp );
3499 #ifdef VIK_CONFIG_GEOTAG
3500 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3502 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3504 // Update directly - not changing the mtime
3505 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3508 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3510 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3513 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3517 * Use code in separate file for this feature as reasonably complex
3519 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3521 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3522 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3523 // Unset so can be reverified later if necessary
3524 vtl->has_verified_thumbnails = FALSE;
3526 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3532 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3534 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3535 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3537 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3543 static void trw_layer_geotagging ( menu_array_layer values )
3545 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3546 // Unset so can be reverified later if necessary
3547 vtl->has_verified_thumbnails = FALSE;
3549 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3556 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3558 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3560 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3561 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3562 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3563 VikViewport *vvp = vik_window_viewport(vw);
3565 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3569 * Acquire into this TRW Layer straight from GPS Device
3571 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3573 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3574 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3578 * Acquire into this TRW Layer from Directions
3580 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3582 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3586 * Acquire into this TRW Layer from an entered URL
3588 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3590 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3591 trw_layer_acquire ( values, &vik_datasource_url_interface );
3594 #ifdef VIK_CONFIG_OPENSTREETMAP
3596 * Acquire into this TRW Layer from OSM
3598 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3600 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3604 * Acquire into this TRW Layer from OSM for 'My' Traces
3606 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3608 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3612 #ifdef VIK_CONFIG_GEOCACHES
3614 * Acquire into this TRW Layer from Geocaching.com
3616 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3618 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3622 #ifdef VIK_CONFIG_GEOTAG
3624 * Acquire into this TRW Layer from images
3626 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3628 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3630 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3631 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3633 // Reverify thumbnails as they may have changed
3634 vtl->has_verified_thumbnails = FALSE;
3635 trw_layer_verify_thumbnails ( vtl, NULL );
3639 static void trw_layer_gps_upload ( menu_array_layer values )
3641 menu_array_sublayer data;
3643 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3645 data[MA_VTL] = values[MA_VTL];
3646 data[MA_VLP] = values[MA_VLP];
3648 trw_layer_gps_upload_any ( data );
3652 * If pass_along[3] is defined that this will upload just that track
3654 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3656 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3657 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3659 // May not actually get a track here as values[2&3] can be null
3660 VikTrack *track = NULL;
3661 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3662 gboolean xfer_all = FALSE;
3664 if ( values[MA_SUBTYPE] ) {
3666 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3667 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3670 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3671 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3674 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3677 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3681 else if ( !values[MA_CONFIRM] )
3682 xfer_all = TRUE; // i.e. whole layer
3684 if (track && !track->visible) {
3685 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3689 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3690 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3691 GTK_DIALOG_DESTROY_WITH_PARENT,
3693 GTK_RESPONSE_ACCEPT,
3695 GTK_RESPONSE_REJECT,
3698 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3699 GtkWidget *response_w = NULL;
3700 #if GTK_CHECK_VERSION (2, 20, 0)
3701 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3705 gtk_widget_grab_focus ( response_w );
3707 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3709 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3710 datasource_gps_clean_up ( dgs );
3711 gtk_widget_destroy ( dialog );
3715 // Get info from reused datasource dialog widgets
3716 gchar* protocol = datasource_gps_get_protocol ( dgs );
3717 gchar* port = datasource_gps_get_descriptor ( dgs );
3718 // NB don't free the above strings as they're references to values held elsewhere
3719 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3720 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3721 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3722 gboolean turn_off = datasource_gps_get_off ( dgs );
3724 gtk_widget_destroy ( dialog );
3726 // When called from the viewport - work the corresponding layerspanel:
3728 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3731 // Apply settings to transfer to the GPS device
3738 vik_layers_panel_get_viewport (vlp),
3747 * Acquire into this TRW Layer from any GPS Babel supported file
3749 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3751 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3752 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3753 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3754 VikViewport *vvp = vik_window_viewport(vw);
3756 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3759 static void trw_layer_new_wp ( menu_array_layer values )
3761 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3762 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3763 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3764 instead return true if you want to update. */
3765 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 ) {
3766 trw_layer_calculate_bounds_waypoints ( vtl );
3767 vik_layers_panel_emit_update ( vlp );
3771 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3773 vtl->current_track = vik_track_new();
3774 vik_track_set_defaults ( vtl->current_track );
3775 vtl->current_track->visible = TRUE;
3776 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3777 // Create track with the preferred colour from the layer properties
3778 vtl->current_track->color = vtl->track_color;
3780 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3781 vtl->current_track->has_color = TRUE;
3782 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3785 static void trw_layer_new_track ( menu_array_layer values )
3787 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3789 if ( ! vtl->current_track ) {
3790 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3791 new_track_create_common ( vtl, name );
3794 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3798 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3800 vtl->current_track = vik_track_new();
3801 vik_track_set_defaults ( vtl->current_track );
3802 vtl->current_track->visible = TRUE;
3803 vtl->current_track->is_route = TRUE;
3804 // By default make all routes red
3805 vtl->current_track->has_color = TRUE;
3806 gdk_color_parse ( "red", &vtl->current_track->color );
3807 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3810 static void trw_layer_new_route ( menu_array_layer values )
3812 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3814 if ( ! vtl->current_track ) {
3815 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3816 new_route_create_common ( vtl, name );
3818 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3822 static void trw_layer_auto_routes_view ( menu_array_layer values )
3824 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3825 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3827 if ( g_hash_table_size (vtl->routes) > 0 ) {
3828 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3829 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3830 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3831 vik_layers_panel_emit_update ( vlp );
3836 static void trw_layer_finish_track ( menu_array_layer values )
3838 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3839 vtl->current_track = NULL;
3840 vik_layer_emit_update ( VIK_LAYER(vtl) );
3843 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3845 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3846 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3848 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3849 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3850 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3851 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3852 vik_layers_panel_emit_update ( vlp );
3856 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3858 /* NB do not care if wp is visible or not */
3859 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3862 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3864 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3865 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3867 /* Only 1 waypoint - jump straight to it */
3868 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3869 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3870 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3872 /* If at least 2 waypoints - find center and then zoom to fit */
3873 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3875 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3876 maxmin[0].lat = vtl->waypoints_bbox.north;
3877 maxmin[1].lat = vtl->waypoints_bbox.south;
3878 maxmin[0].lon = vtl->waypoints_bbox.east;
3879 maxmin[1].lon = vtl->waypoints_bbox.west;
3880 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3883 vik_layers_panel_emit_update ( vlp );
3886 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3888 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3891 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3893 if ( values[MA_MISC] ) {
3894 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3895 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3899 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3901 static menu_array_layer pass_along;
3903 GtkWidget *export_submenu;
3904 pass_along[MA_VTL] = vtl;
3905 pass_along[MA_VLP] = vlp;
3907 item = gtk_menu_item_new();
3908 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3909 gtk_widget_show ( item );
3911 if ( vtl->current_track ) {
3912 if ( vtl->current_track->is_route )
3913 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3915 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3916 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3917 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3918 gtk_widget_show ( item );
3921 item = gtk_menu_item_new ();
3922 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3923 gtk_widget_show ( item );
3926 /* Now with icons */
3927 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3928 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3930 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3931 gtk_widget_show ( item );
3933 GtkWidget *view_submenu = gtk_menu_new();
3934 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3935 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3936 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3937 gtk_widget_show ( item );
3938 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3940 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3941 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3942 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3943 gtk_widget_show ( item );
3945 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3947 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3948 gtk_widget_show ( item );
3950 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3952 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3953 gtk_widget_show ( item );
3955 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3956 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3958 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3959 gtk_widget_show ( item );
3961 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3963 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3964 gtk_widget_show ( item );
3966 export_submenu = gtk_menu_new ();
3967 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3968 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3969 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3970 gtk_widget_show ( item );
3971 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3973 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3975 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3976 gtk_widget_show ( item );
3978 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3980 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3981 gtk_widget_show ( item );
3983 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3985 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3986 gtk_widget_show ( item );
3988 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3990 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3991 gtk_widget_show ( item );
3993 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
3994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
3995 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3996 gtk_widget_show ( item );
3998 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3999 item = gtk_menu_item_new_with_mnemonic ( external1 );
4000 g_free ( external1 );
4001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4002 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4003 gtk_widget_show ( item );
4005 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4006 item = gtk_menu_item_new_with_mnemonic ( external2 );
4007 g_free ( external2 );
4008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4010 gtk_widget_show ( item );
4012 GtkWidget *new_submenu = gtk_menu_new();
4013 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4015 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4016 gtk_widget_show(item);
4017 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4019 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4020 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4021 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4022 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4023 gtk_widget_show ( item );
4025 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4026 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4028 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4029 gtk_widget_show ( item );
4030 // Make it available only when a new track *not* already in progress
4031 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4033 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4034 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4036 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4037 gtk_widget_show ( item );
4038 // Make it available only when a new track *not* already in progress
4039 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4041 #ifdef VIK_CONFIG_GEOTAG
4042 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4044 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4045 gtk_widget_show ( item );
4048 GtkWidget *acquire_submenu = gtk_menu_new ();
4049 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4051 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4052 gtk_widget_show ( item );
4053 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4055 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4057 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4058 gtk_widget_show ( item );
4060 /* FIXME: only add menu when at least a routing engine has support for Directions */
4061 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4063 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4064 gtk_widget_show ( item );
4066 #ifdef VIK_CONFIG_OPENSTREETMAP
4067 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4069 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4070 gtk_widget_show ( item );
4072 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4074 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4075 gtk_widget_show ( item );
4078 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4080 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4081 gtk_widget_show ( item );
4083 #ifdef VIK_CONFIG_GEONAMES
4084 GtkWidget *wikipedia_submenu = gtk_menu_new();
4085 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4086 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4087 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4088 gtk_widget_show(item);
4089 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4091 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4092 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4094 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4095 gtk_widget_show ( item );
4097 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4100 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4101 gtk_widget_show ( item );
4104 #ifdef VIK_CONFIG_GEOCACHES
4105 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4107 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4108 gtk_widget_show ( item );
4111 #ifdef VIK_CONFIG_GEOTAG
4112 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_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 _File...") );
4119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4120 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4121 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4122 gtk_widget_show ( item );
4124 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4126 GtkWidget *upload_submenu = gtk_menu_new ();
4127 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4128 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4129 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4130 gtk_widget_show ( item );
4131 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4133 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4136 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4137 gtk_widget_show ( item );
4139 #ifdef VIK_CONFIG_OPENSTREETMAP
4140 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4144 gtk_widget_show ( item );
4147 GtkWidget *delete_submenu = gtk_menu_new ();
4148 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4151 gtk_widget_show ( item );
4152 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4154 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4155 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4157 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4158 gtk_widget_show ( item );
4160 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4163 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4164 gtk_widget_show ( item );
4166 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4167 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4169 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4170 gtk_widget_show ( item );
4172 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4173 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4175 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4176 gtk_widget_show ( item );
4178 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4179 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4181 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4182 gtk_widget_show ( item );
4184 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4185 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4187 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4188 gtk_widget_show ( item );
4190 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4191 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4193 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4194 gtk_widget_show ( item );
4197 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4198 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4200 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4201 gtk_widget_show ( item );
4204 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4207 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4208 gtk_widget_show ( item );
4209 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4211 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4212 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4214 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4215 gtk_widget_show ( item );
4216 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4219 // Fake Waypoint UUIDs vi simple increasing integer
4220 static guint wp_uuid = 0;
4222 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4226 vik_waypoint_set_name (wp, name);
4228 if ( VIK_LAYER(vtl)->realized )
4230 // Do we need to create the sublayer:
4231 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4232 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4235 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4237 // Visibility column always needed for waypoints
4238 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 );
4240 // Actual setting of visibility dependent on the waypoint
4241 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4243 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4245 // Sort now as post_read is not called on a realized waypoint
4246 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4249 highest_wp_number_add_wp(vtl, name);
4250 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4254 // Fake Track UUIDs vi simple increasing integer
4255 static guint tr_uuid = 0;
4257 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4261 vik_track_set_name (t, name);
4263 if ( VIK_LAYER(vtl)->realized )
4265 // Do we need to create the sublayer:
4266 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4267 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4270 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4271 // Visibility column always needed for tracks
4272 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 );
4274 // Actual setting of visibility dependent on the track
4275 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4277 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4279 // Sort now as post_read is not called on a realized track
4280 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4283 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4285 trw_layer_update_treeview ( vtl, t );
4288 // Fake Route UUIDs vi simple increasing integer
4289 static guint rt_uuid = 0;
4291 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4295 vik_track_set_name (t, name);
4297 if ( VIK_LAYER(vtl)->realized )
4299 // Do we need to create the sublayer:
4300 if ( g_hash_table_size (vtl->routes) == 0 ) {
4301 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4304 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4305 // Visibility column always needed for routes
4306 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 );
4307 // Actual setting of visibility dependent on the route
4308 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4310 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4312 // Sort now as post_read is not called on a realized route
4313 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4316 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4318 trw_layer_update_treeview ( vtl, t );
4321 /* to be called whenever a track has been deleted or may have been changed. */
4322 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4324 if (vtl->current_tp_track == trk )
4325 trw_layer_cancel_current_tp ( vtl, FALSE );
4329 * Normally this is done to due the waypoint size preference having changed
4331 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4333 GHashTableIter iter;
4334 gpointer key, value;
4337 g_hash_table_iter_init ( &iter, vtl->waypoints );
4338 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4339 VikWaypoint *wp = VIK_WAYPOINT(value);
4341 // Reapply symbol setting to update the pixbuf
4342 gchar *tmp_symbol = g_strdup ( wp->symbol );
4343 vik_waypoint_set_symbol ( wp, tmp_symbol );
4344 g_free ( tmp_symbol );
4350 * trw_layer_new_unique_sublayer_name:
4352 * Allocates a unique new name
4354 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4357 gchar *newname = g_strdup(name);
4362 switch ( sublayer_type ) {
4363 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4364 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4366 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4367 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4370 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4373 // If found a name already in use try adding 1 to it and we try again
4375 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4377 newname = new_newname;
4380 } while ( id != NULL);
4385 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4387 // No more uniqueness of name forced when loading from a file
4388 // This now makes this function a little redunant as we just flow the parameters through
4389 vik_trw_layer_add_waypoint ( vtl, name, wp );
4392 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4394 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4395 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4396 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4397 vik_track_free ( tr );
4398 vtl->route_finder_append = FALSE; /* this means we have added it */
4401 // No more uniqueness of name forced when loading from a file
4403 vik_trw_layer_add_route ( vtl, name, tr );
4405 vik_trw_layer_add_track ( vtl, name, tr );
4407 if ( vtl->route_finder_check_added_track ) {
4408 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4409 vtl->route_finder_added_track = tr;
4414 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4416 *l = g_list_append(*l, id);
4420 * Move an item from one TRW layer to another TRW layer
4422 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4424 // TODO reconsider strategy when moving within layer (if anything...)
4425 gboolean rename = ( vtl_src != vtl_dest );
4429 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4430 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4434 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4436 newname = g_strdup ( trk->name );
4438 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4439 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4441 vik_trw_layer_delete_track ( vtl_src, trk );
4444 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4445 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4449 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4451 newname = g_strdup ( trk->name );
4453 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4454 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4456 vik_trw_layer_delete_route ( vtl_src, trk );
4459 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4460 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4464 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4466 newname = g_strdup ( wp->name );
4468 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4469 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4471 trw_layer_delete_waypoint ( vtl_src, wp );
4473 // Recalculate bounds even if not renamed as maybe dragged between layers
4474 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4475 trw_layer_calculate_bounds_waypoints ( vtl_src );
4479 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4481 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4482 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4484 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4485 GList *items = NULL;
4488 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4489 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4491 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4492 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4494 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4495 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4500 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4501 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4502 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4503 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4505 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4512 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4513 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4517 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4519 trku_udata *user_data = udata;
4520 if ( trk == user_data->trk ) {
4521 user_data->uuid = id;
4527 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4529 gboolean was_visible = FALSE;
4530 if ( trk && trk->name ) {
4532 if ( trk == vtl->current_track ) {
4533 vtl->current_track = NULL;
4534 vtl->current_tp_track = NULL;
4535 vtl->current_tp_id = NULL;
4536 vtl->moving_tp = FALSE;
4539 was_visible = trk->visible;
4541 if ( trk == vtl->route_finder_current_track )
4542 vtl->route_finder_current_track = NULL;
4544 if ( trk == vtl->route_finder_added_track )
4545 vtl->route_finder_added_track = NULL;
4551 // Hmmm, want key of it
4552 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4554 if ( trkf && udata.uuid ) {
4555 /* could be current_tp, so we have to check */
4556 trw_layer_cancel_tps_of_track ( vtl, trk );
4558 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4561 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4562 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4563 g_hash_table_remove ( vtl->tracks, udata.uuid );
4565 // If last sublayer, then remove sublayer container
4566 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4567 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4570 // Incase it was selected (no item delete signal ATM)
4571 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4577 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4579 gboolean was_visible = FALSE;
4581 if ( trk && trk->name ) {
4583 if ( trk == vtl->current_track ) {
4584 vtl->current_track = NULL;
4585 vtl->current_tp_track = NULL;
4586 vtl->current_tp_id = NULL;
4587 vtl->moving_tp = FALSE;
4590 was_visible = trk->visible;
4592 if ( trk == vtl->route_finder_current_track )
4593 vtl->route_finder_current_track = NULL;
4595 if ( trk == vtl->route_finder_added_track )
4596 vtl->route_finder_added_track = NULL;
4602 // Hmmm, want key of it
4603 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4605 if ( trkf && udata.uuid ) {
4606 /* could be current_tp, so we have to check */
4607 trw_layer_cancel_tps_of_track ( vtl, trk );
4609 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4612 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4613 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4614 g_hash_table_remove ( vtl->routes, udata.uuid );
4616 // If last sublayer, then remove sublayer container
4617 if ( g_hash_table_size (vtl->routes) == 0 ) {
4618 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4621 // Incase it was selected (no item delete signal ATM)
4622 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4628 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4630 gboolean was_visible = FALSE;
4632 if ( wp && wp->name ) {
4634 if ( wp == vtl->current_wp ) {
4635 vtl->current_wp = NULL;
4636 vtl->current_wp_id = NULL;
4637 vtl->moving_wp = FALSE;
4640 was_visible = wp->visible;
4646 // Hmmm, want key of it
4647 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4649 if ( wpf && udata.uuid ) {
4650 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4653 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4654 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4656 highest_wp_number_remove_wp(vtl, wp->name);
4657 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4659 // If last sublayer, then remove sublayer container
4660 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4661 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4664 // Incase it was selected (no item delete signal ATM)
4665 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4673 // Only for temporary use by trw_layer_delete_waypoint_by_name
4674 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4676 wpu_udata *user_data = udata;
4677 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4678 user_data->uuid = id;
4685 * Delete a waypoint by the given name
4686 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4687 * as there be multiple waypoints with the same name
4689 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4692 // Fake a waypoint with the given name
4693 udata.wp = vik_waypoint_new ();
4694 vik_waypoint_set_name (udata.wp, name);
4695 // Currently only the name is used in this waypoint find function
4698 // Hmmm, want key of it
4699 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4701 vik_waypoint_free (udata.wp);
4703 if ( wpf && udata.uuid )
4704 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4710 VikTrack *trk; // input
4711 gpointer uuid; // output
4714 // Only for temporary use by trw_layer_delete_track_by_name
4715 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4717 tpu_udata *user_data = udata;
4718 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4719 user_data->uuid = id;
4726 * Delete a track by the given name
4727 * NOTE: ATM this will delete the first encountered Track with the specified name
4728 * as there may be multiple tracks with the same name within the specified hash table
4730 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4733 // Fake a track with the given name
4734 udata.trk = vik_track_new ();
4735 vik_track_set_name (udata.trk, name);
4736 // Currently only the name is used in this waypoint find function
4739 // Hmmm, want key of it
4740 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4742 vik_track_free (udata.trk);
4744 if ( trkf && udata.uuid ) {
4745 // This could be a little better written...
4746 if ( vtl->tracks == ht_tracks )
4747 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4748 if ( vtl->routes == ht_tracks )
4749 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4756 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4758 vik_treeview_item_delete (vt, it );
4761 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4764 vtl->current_track = NULL;
4765 vtl->route_finder_current_track = NULL;
4766 vtl->route_finder_added_track = NULL;
4767 if (vtl->current_tp_track)
4768 trw_layer_cancel_current_tp(vtl, FALSE);
4770 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4771 g_hash_table_remove_all(vtl->routes_iters);
4772 g_hash_table_remove_all(vtl->routes);
4774 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4776 vik_layer_emit_update ( VIK_LAYER(vtl) );
4779 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4782 vtl->current_track = NULL;
4783 vtl->route_finder_current_track = NULL;
4784 vtl->route_finder_added_track = NULL;
4785 if (vtl->current_tp_track)
4786 trw_layer_cancel_current_tp(vtl, FALSE);
4788 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4789 g_hash_table_remove_all(vtl->tracks_iters);
4790 g_hash_table_remove_all(vtl->tracks);
4792 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4794 vik_layer_emit_update ( VIK_LAYER(vtl) );
4797 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4799 vtl->current_wp = NULL;
4800 vtl->current_wp_id = NULL;
4801 vtl->moving_wp = FALSE;
4803 highest_wp_number_reset(vtl);
4805 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4806 g_hash_table_remove_all(vtl->waypoints_iters);
4807 g_hash_table_remove_all(vtl->waypoints);
4809 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4811 vik_layer_emit_update ( VIK_LAYER(vtl) );
4814 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4816 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4817 // Get confirmation from the user
4818 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4819 _("Are you sure you want to delete all tracks in %s?"),
4820 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4821 vik_trw_layer_delete_all_tracks (vtl);
4824 static void trw_layer_delete_all_routes ( menu_array_layer values )
4826 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4827 // Get confirmation from the user
4828 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4829 _("Are you sure you want to delete all routes in %s?"),
4830 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4831 vik_trw_layer_delete_all_routes (vtl);
4834 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4836 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4837 // Get confirmation from the user
4838 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4839 _("Are you sure you want to delete all waypoints in %s?"),
4840 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4841 vik_trw_layer_delete_all_waypoints (vtl);
4844 static void trw_layer_delete_item ( menu_array_sublayer values )
4846 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4847 gboolean was_visible = FALSE;
4848 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4850 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4851 if ( wp && wp->name ) {
4852 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4853 // Get confirmation from the user
4854 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4855 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4856 _("Are you sure you want to delete the waypoint \"%s\"?"),
4859 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4862 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4864 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4865 if ( trk && trk->name ) {
4866 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
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 the track \"%s\"?"),
4872 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4877 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4878 if ( trk && trk->name ) {
4879 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4880 // Get confirmation from the user
4881 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4882 _("Are you sure you want to delete the route \"%s\"?"),
4885 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4889 vik_layer_emit_update ( VIK_LAYER(vtl) );
4893 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4895 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4897 vik_waypoint_set_name ( wp, new_name );
4899 // Now update the treeview as well
4904 // Need key of it for treeview update
4905 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4907 if ( wpf && udataU.uuid ) {
4908 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4911 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4912 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4918 * Maintain icon of waypoint in the treeview
4920 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4922 // update the treeview
4927 // Need key of it for treeview update
4928 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4930 if ( wpf && udataU.uuid ) {
4931 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4934 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4939 static void trw_layer_properties_item ( menu_array_sublayer values )
4941 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4942 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4944 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4946 if ( wp && wp->name )
4948 gboolean updated = FALSE;
4949 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4951 trw_layer_waypoint_rename ( vtl, wp, new_name );
4953 if ( updated && values[MA_TV_ITER] )
4954 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4956 if ( updated && VIK_LAYER(vtl)->visible )
4957 vik_layer_emit_update ( VIK_LAYER(vtl) );
4963 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4964 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4966 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4968 if ( tr && tr->name )
4970 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4981 * trw_layer_track_statistics:
4983 * Show track statistics.
4984 * ATM jump to the stats page in the properties
4985 * TODO: consider separating the stats into an individual dialog?
4987 static void trw_layer_track_statistics ( menu_array_sublayer values )
4989 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4991 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4992 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4994 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4996 if ( trk && trk->name ) {
4997 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5007 * Update the treeview of the track id - primarily to update the icon
5009 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5015 gpointer *trkf = NULL;
5016 if ( trk->is_route )
5017 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5019 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5021 if ( trkf && udata.uuid ) {
5023 GtkTreeIter *iter = NULL;
5024 if ( trk->is_route )
5025 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5027 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5030 // TODO: Make this a function
5031 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5032 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5033 ((trk->color.green & 0xff00) << 8) |
5034 (trk->color.blue & 0xff00);
5035 gdk_pixbuf_fill ( pixbuf, pixel );
5036 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5037 g_object_unref (pixbuf);
5044 Parameter 1 -> VikLayersPanel
5045 Parameter 2 -> VikLayer
5046 Parameter 3 -> VikViewport
5048 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5051 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
5052 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5055 /* since vlp not set, vl & vvp should be valid instead! */
5057 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
5058 vik_layer_emit_update ( VIK_LAYER(vl) );
5063 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5065 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5067 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5068 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5070 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5072 if ( track && track->trackpoints )
5073 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5076 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5078 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5080 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5081 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5083 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5085 if ( track && track->trackpoints )
5087 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5089 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5090 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5091 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5092 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5093 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5097 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5099 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5101 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5102 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5104 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5109 // Converting a track to a route can be a bit more complicated,
5110 // so give a chance to change our minds:
5111 if ( !trk->is_route &&
5112 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5113 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5115 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5116 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5121 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5124 trk_copy->is_route = !trk_copy->is_route;
5126 // ATM can't set name to self - so must create temporary copy
5127 gchar *name = g_strdup ( trk_copy->name );
5129 // Delete old one and then add new one
5130 if ( trk->is_route ) {
5131 vik_trw_layer_delete_route ( vtl, trk );
5132 vik_trw_layer_add_track ( vtl, name, trk_copy );
5135 // Extra route conversion bits...
5136 vik_track_merge_segments ( trk_copy );
5137 vik_track_to_routepoints ( trk_copy );
5139 vik_trw_layer_delete_track ( vtl, trk );
5140 vik_trw_layer_add_route ( vtl, name, trk_copy );
5144 // Update in case color of track / route changes when moving between sublayers
5145 vik_layer_emit_update ( VIK_LAYER(vtl) );
5148 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5150 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5152 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5153 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5155 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5158 vik_track_anonymize_times ( track );
5161 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5163 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5165 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5166 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5168 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5173 vtl->current_track = track;
5174 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);
5176 if ( track->trackpoints )
5177 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5181 * extend a track using route finder
5183 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5185 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5186 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5189 if ( !track->trackpoints )
5192 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5193 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5194 vtl->route_finder_current_track = track;
5195 vtl->route_finder_started = TRUE;
5197 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5203 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5205 // If have a vlp then perform a basic test to see if any DEM info available...
5207 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5209 if ( !g_list_length(dems) ) {
5210 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5218 * apply_dem_data_common:
5220 * A common function for applying the DEM values and reporting the results.
5222 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5224 if ( !trw_layer_dem_test ( vtl, vlp ) )
5227 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5228 // Inform user how much was changed
5230 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5231 g_snprintf(str, 64, tmp_str, changed);
5232 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5235 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5237 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5239 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5240 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5242 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5245 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5248 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5250 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5252 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5253 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5255 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5258 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5264 * A common function for applying the elevation smoothing and reporting the results.
5266 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5268 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5269 // Inform user how much was changed
5271 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5272 g_snprintf(str, 64, tmp_str, changed);
5273 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5279 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5281 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5283 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5284 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5286 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5291 smooth_it ( vtl, track, FALSE );
5294 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5296 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5298 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5299 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5301 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5306 smooth_it ( vtl, track, TRUE );
5310 * Commonal helper function
5312 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5315 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5316 g_snprintf(str, 64, tmp_str, changed);
5317 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5320 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5322 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5323 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5325 if ( !trw_layer_dem_test ( vtl, vlp ) )
5329 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5331 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5333 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5337 GHashTableIter iter;
5338 gpointer key, value;
5340 g_hash_table_iter_init ( &iter, vtl->waypoints );
5341 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5342 VikWaypoint *wp = VIK_WAYPOINT(value);
5343 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5346 wp_changed_message ( vtl, changed );
5349 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5351 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5352 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5354 if ( !trw_layer_dem_test ( vtl, vlp ) )
5358 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5360 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5362 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5366 GHashTableIter iter;
5367 gpointer key, value;
5369 g_hash_table_iter_init ( &iter, vtl->waypoints );
5370 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5371 VikWaypoint *wp = VIK_WAYPOINT(value);
5372 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5375 wp_changed_message ( vtl, changed );
5378 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5380 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5382 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5383 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5385 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5389 if ( !track->trackpoints )
5391 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5394 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5396 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5398 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5399 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5401 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5406 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5409 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5412 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5414 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5416 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5417 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5419 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5424 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5427 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5430 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5432 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5434 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5435 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5437 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5442 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5445 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5449 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5451 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5453 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5455 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5456 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5458 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5460 if ( trk && trk->trackpoints )
5462 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5463 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5464 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5465 if ( values[MA_VLP] )
5466 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5468 vik_layer_emit_update ( VIK_LAYER(vtl) );
5473 * Refine the selected track/route with a routing engine.
5474 * The routing engine is selected by the user, when requestiong the job.
5476 static void trw_layer_route_refine ( menu_array_sublayer values )
5478 static gint last_engine = 0;
5479 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5482 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5483 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5485 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5487 if ( trk && trk->trackpoints )
5489 /* Check size of the route */
5490 int nb = vik_track_get_tp_count(trk);
5492 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5493 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5494 GTK_MESSAGE_WARNING,
5495 GTK_BUTTONS_OK_CANCEL,
5496 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5498 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5499 gtk_widget_destroy ( dialog );
5500 if (response != GTK_RESPONSE_OK )
5503 /* Select engine from dialog */
5504 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5505 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5506 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5508 GTK_RESPONSE_REJECT,
5510 GTK_RESPONSE_ACCEPT,
5512 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5513 gtk_widget_show_all(label);
5515 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5517 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5518 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5519 gtk_widget_show_all(combo);
5521 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5523 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5525 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5527 /* Dialog validated: retrieve selected engine and do the job */
5528 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5529 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5532 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5534 /* Force saving track */
5535 /* FIXME: remove or rename this hack */
5536 vtl->route_finder_check_added_track = TRUE;
5539 vik_routing_engine_refine (routing, vtl, trk);
5541 /* FIXME: remove or rename this hack */
5542 if ( vtl->route_finder_added_track )
5543 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5545 vtl->route_finder_added_track = NULL;
5546 vtl->route_finder_check_added_track = FALSE;
5548 vik_layer_emit_update ( VIK_LAYER(vtl) );
5550 /* Restore cursor */
5551 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5553 gtk_widget_destroy ( dialog );
5557 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5559 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5560 trw_layer_tpwin_init ( vtl );
5563 /*************************************
5564 * merge/split by time routines
5565 *************************************/
5567 /* called for each key in track hash table.
5568 * If the current track has the same time stamp type, add it to the result,
5569 * except the one pointed by "exclude".
5570 * set exclude to NULL if there is no exclude to check.
5571 * Note that the result is in reverse (for performance reasons).
5576 gboolean with_timestamps;
5578 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5580 twt_udata *user_data = udata;
5581 VikTrackpoint *p1, *p2;
5582 VikTrack *trk = VIK_TRACK(value);
5583 if (trk == (VikTrack *)user_data->exclude) {
5587 if (trk->trackpoints) {
5588 p1 = vik_track_get_tp_first(trk);
5589 p2 = vik_track_get_tp_last(trk);
5591 if ( user_data->with_timestamps ) {
5592 if (!p1->has_timestamp || !p2->has_timestamp) {
5597 // Don't add tracks with timestamps when getting non timestamp tracks
5598 if (p1->has_timestamp || p2->has_timestamp) {
5604 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5607 /* called for each key in track hash table. if original track user_data[1] is close enough
5608 * to the passed one, add it to list in user_data[0]
5610 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5613 VikTrackpoint *p1, *p2;
5614 VikTrack *trk = VIK_TRACK(value);
5616 GList **nearby_tracks = ((gpointer *)user_data)[0];
5617 GList *tpoints = ((gpointer *)user_data)[1];
5620 * detect reasons for not merging, and return
5621 * if no reason is found not to merge, then do it.
5624 // Exclude the original track from the compiled list
5625 if (trk->trackpoints == tpoints) {
5629 t1 = vik_track_get_tp_first(trk)->timestamp;
5630 t2 = vik_track_get_tp_last(trk)->timestamp;
5632 if (trk->trackpoints) {
5633 p1 = vik_track_get_tp_first(trk);
5634 p2 = vik_track_get_tp_last(trk);
5636 if (!p1->has_timestamp || !p2->has_timestamp) {
5637 //g_print("no timestamp\n");
5641 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5642 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5643 if (! (abs(t1 - p2->timestamp) < threshold ||
5645 abs(p1->timestamp - t2) < threshold)
5652 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5655 /* comparison function used to sort tracks; a and b are hash table keys */
5656 /* Not actively used - can be restored if needed
5657 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5659 GHashTable *tracks = user_data;
5662 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5663 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5665 if (t1 < t2) return -1;
5666 if (t1 > t2) return 1;
5671 /* comparison function used to sort trackpoints */
5672 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5674 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5676 if (t1 < t2) return -1;
5677 if (t1 > t2) return 1;
5682 * comparison function which can be used to sort tracks or waypoints by name
5684 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5686 const gchar* namea = (const gchar*) a;
5687 const gchar* nameb = (const gchar*) b;
5688 if ( namea == NULL || nameb == NULL)
5691 // Same sort method as used in the vik_treeview_*_alphabetize functions
5692 return strcmp ( namea, nameb );
5696 * Attempt to merge selected track with other tracks specified by the user
5697 * Tracks to merge with must be of the same 'type' as the selected track -
5698 * either all with timestamps, or all without timestamps
5700 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5702 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5703 GList *other_tracks = NULL;
5704 GHashTable *ght_tracks;
5705 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5706 ght_tracks = vtl->routes;
5708 ght_tracks = vtl->tracks;
5710 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5715 if ( !track->trackpoints )
5719 udata.result = &other_tracks;
5720 udata.exclude = track->trackpoints;
5721 // Allow merging with 'similar' time type time tracks
5722 // i.e. either those times, or those without
5723 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5725 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5726 other_tracks = g_list_reverse(other_tracks);
5728 if ( !other_tracks ) {
5729 if ( udata.with_timestamps )
5730 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5732 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5736 // Sort alphabetically for user presentation
5737 // Convert into list of names for usage with dialog function
5738 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5739 GList *other_tracks_names = NULL;
5740 GList *iter = g_list_first ( other_tracks );
5742 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5743 iter = g_list_next ( iter );
5746 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5748 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5752 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5753 g_list_free(other_tracks);
5754 g_list_free(other_tracks_names);
5759 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5760 VikTrack *merge_track;
5761 if ( track->is_route )
5762 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5764 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5767 vik_track_steal_and_append_trackpoints ( track, merge_track );
5768 if ( track->is_route )
5769 vik_trw_layer_delete_route (vtl, merge_track);
5771 vik_trw_layer_delete_track (vtl, merge_track);
5772 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5775 for (l = merge_list; l != NULL; l = g_list_next(l))
5777 g_list_free(merge_list);
5779 vik_layer_emit_update( VIK_LAYER(vtl) );
5783 // c.f. trw_layer_sorted_track_id_by_name_list
5784 // but don't add the specified track to the list (normally current track)
5785 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5787 twt_udata *user_data = udata;
5790 if (trk->trackpoints == user_data->exclude) {
5794 // Sort named list alphabetically
5795 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5799 * Join - this allows combining 'tracks' and 'track routes'
5800 * i.e. doesn't care about whether tracks have consistent timestamps
5801 * ATM can only append one track at a time to the currently selected track
5803 static void trw_layer_append_track ( menu_array_sublayer values )
5806 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5808 GHashTable *ght_tracks;
5809 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5810 ght_tracks = vtl->routes;
5812 ght_tracks = vtl->tracks;
5814 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5819 GList *other_tracks_names = NULL;
5821 // Sort alphabetically for user presentation
5822 // Convert into list of names for usage with dialog function
5823 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5825 udata.result = &other_tracks_names;
5826 udata.exclude = trk->trackpoints;
5828 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5830 // Note the limit to selecting one track only
5831 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5832 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5833 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5836 trk->is_route ? _("Append Route"): _("Append Track"),
5837 trk->is_route ? _("Select the route to append after the current route") :
5838 _("Select the track to append after the current track") );
5840 g_list_free(other_tracks_names);
5842 // It's a list, but shouldn't contain more than one other track!
5843 if ( append_list ) {
5845 for (l = append_list; l != NULL; l = g_list_next(l)) {
5846 // TODO: at present this uses the first track found by name,
5847 // which with potential multiple same named tracks may not be the one selected...
5848 VikTrack *append_track;
5849 if ( trk->is_route )
5850 append_track = vik_trw_layer_get_route ( vtl, l->data );
5852 append_track = vik_trw_layer_get_track ( vtl, l->data );
5854 if ( append_track ) {
5855 vik_track_steal_and_append_trackpoints ( trk, append_track );
5856 if ( trk->is_route )
5857 vik_trw_layer_delete_route (vtl, append_track);
5859 vik_trw_layer_delete_track (vtl, append_track);
5862 for (l = append_list; l != NULL; l = g_list_next(l))
5864 g_list_free(append_list);
5866 vik_layer_emit_update( VIK_LAYER(vtl) );
5871 * Very similar to trw_layer_append_track for joining
5872 * but this allows selection from the 'other' list
5873 * If a track is selected, then is shows routes and joins the selected one
5874 * If a route is selected, then is shows tracks and joins the selected one
5876 static void trw_layer_append_other ( menu_array_sublayer values )
5879 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5881 GHashTable *ght_mykind, *ght_others;
5882 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5883 ght_mykind = vtl->routes;
5884 ght_others = vtl->tracks;
5887 ght_mykind = vtl->tracks;
5888 ght_others = vtl->routes;
5891 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5896 GList *other_tracks_names = NULL;
5898 // Sort alphabetically for user presentation
5899 // Convert into list of names for usage with dialog function
5900 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5902 udata.result = &other_tracks_names;
5903 udata.exclude = trk->trackpoints;
5905 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5907 // Note the limit to selecting one track only
5908 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5909 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5910 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5913 trk->is_route ? _("Append Track"): _("Append Route"),
5914 trk->is_route ? _("Select the track to append after the current route") :
5915 _("Select the route to append after the current track") );
5917 g_list_free(other_tracks_names);
5919 // It's a list, but shouldn't contain more than one other track!
5920 if ( append_list ) {
5922 for (l = append_list; l != NULL; l = g_list_next(l)) {
5923 // TODO: at present this uses the first track found by name,
5924 // which with potential multiple same named tracks may not be the one selected...
5926 // Get FROM THE OTHER TYPE list
5927 VikTrack *append_track;
5928 if ( trk->is_route )
5929 append_track = vik_trw_layer_get_track ( vtl, l->data );
5931 append_track = vik_trw_layer_get_route ( vtl, l->data );
5933 if ( append_track ) {
5935 if ( !append_track->is_route &&
5936 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5937 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5939 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5940 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5941 vik_track_merge_segments ( append_track );
5942 vik_track_to_routepoints ( append_track );
5949 vik_track_steal_and_append_trackpoints ( trk, append_track );
5951 // Delete copied which is FROM THE OTHER TYPE list
5952 if ( trk->is_route )
5953 vik_trw_layer_delete_track (vtl, append_track);
5955 vik_trw_layer_delete_route (vtl, append_track);
5958 for (l = append_list; l != NULL; l = g_list_next(l))
5960 g_list_free(append_list);
5961 vik_layer_emit_update( VIK_LAYER(vtl) );
5965 /* merge by segments */
5966 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5968 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5969 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5970 guint segments = vik_track_merge_segments ( trk );
5971 // NB currently no need to redraw as segments not actually shown on the display
5972 // However inform the user of what happened:
5974 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5975 g_snprintf(str, 64, tmp_str, segments);
5976 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5979 /* merge by time routine */
5980 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5982 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5986 GList *tracks_with_timestamp = NULL;
5987 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5988 if (orig_trk->trackpoints &&
5989 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
5990 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5995 udata.result = &tracks_with_timestamp;
5996 udata.exclude = orig_trk->trackpoints;
5997 udata.with_timestamps = TRUE;
5998 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5999 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6001 if (!tracks_with_timestamp) {
6002 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6005 g_list_free(tracks_with_timestamp);
6007 static guint threshold_in_minutes = 1;
6008 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6009 _("Merge Threshold..."),
6010 _("Merge when time between tracks less than:"),
6011 &threshold_in_minutes)) {
6015 // keep attempting to merge all tracks until no merges within the time specified is possible
6016 gboolean attempt_merge = TRUE;
6017 GList *nearby_tracks = NULL;
6019 static gpointer params[3];
6021 while ( attempt_merge ) {
6023 // Don't try again unless tracks have changed
6024 attempt_merge = FALSE;
6026 trps = orig_trk->trackpoints;
6030 if (nearby_tracks) {
6031 g_list_free(nearby_tracks);
6032 nearby_tracks = NULL;
6035 params[0] = &nearby_tracks;
6036 params[1] = (gpointer)trps;
6037 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6039 /* get a list of adjacent-in-time tracks */
6040 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6043 GList *l = nearby_tracks;
6045 /* remove trackpoints from merged track, delete track */
6046 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6047 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6049 // Tracks have changed, therefore retry again against all the remaining tracks
6050 attempt_merge = TRUE;
6055 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6058 g_list_free(nearby_tracks);
6060 vik_layer_emit_update( VIK_LAYER(vtl) );
6064 * Split a track at the currently selected trackpoint
6066 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6068 if ( !vtl->current_tpl )
6071 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6072 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6074 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6075 GList *newglist = g_list_alloc ();
6076 newglist->prev = NULL;
6077 newglist->next = vtl->current_tpl->next;
6078 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6079 tr->trackpoints = newglist;
6081 vtl->current_tpl->next->prev = newglist; /* end old track here */
6082 vtl->current_tpl->next = NULL;
6084 // Bounds of the selected track changed due to the split
6085 vik_track_calculate_bounds ( vtl->current_tp_track );
6087 vtl->current_tpl = newglist; /* change tp to first of new track. */
6088 vtl->current_tp_track = tr;
6091 vik_trw_layer_add_route ( vtl, name, tr );
6093 vik_trw_layer_add_track ( vtl, name, tr );
6095 // Bounds of the new track created by the split
6096 vik_track_calculate_bounds ( tr );
6102 // Also need id of newly created track
6105 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6107 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6109 if ( trkf && udata.uuid )
6110 vtl->current_tp_id = udata.uuid;
6112 vtl->current_tp_id = NULL;
6114 vik_layer_emit_update(VIK_LAYER(vtl));
6120 /* split by time routine */
6121 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6123 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6124 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6125 GList *trps = track->trackpoints;
6127 GList *newlists = NULL;
6128 GList *newtps = NULL;
6129 static guint thr = 1;
6136 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6137 _("Split Threshold..."),
6138 _("Split when time between trackpoints exceeds:"),
6143 /* iterate through trackpoints, and copy them into new lists without touching original list */
6144 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6148 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6150 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6153 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6154 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6155 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6157 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6162 if (ts - prev_ts > thr*60) {
6163 /* flush accumulated trackpoints into new list */
6164 newlists = g_list_append(newlists, g_list_reverse(newtps));
6168 /* accumulate trackpoint copies in newtps, in reverse order */
6169 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6171 iter = g_list_next(iter);
6174 newlists = g_list_append(newlists, g_list_reverse(newtps));
6177 /* put lists of trackpoints into tracks */
6179 // Only bother updating if the split results in new tracks
6180 if (g_list_length (newlists) > 1) {
6185 tr = vik_track_copy ( track, FALSE );
6186 tr->trackpoints = (GList *)(iter->data);
6188 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6189 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6190 g_free ( new_tr_name );
6191 vik_track_calculate_bounds ( tr );
6192 iter = g_list_next(iter);
6194 // Remove original track and then update the display
6195 vik_trw_layer_delete_track (vtl, track);
6196 vik_layer_emit_update(VIK_LAYER(vtl));
6198 g_list_free(newlists);
6202 * Split a track by the number of points as specified by the user
6204 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6206 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6208 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6209 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6211 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6216 // Check valid track
6217 GList *trps = track->trackpoints;
6221 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6222 _("Split Every Nth Point"),
6223 _("Split on every Nth point:"),
6224 250, // Default value as per typical limited track capacity of various GPS devices
6228 // Was a valid number returned?
6234 GList *newlists = NULL;
6235 GList *newtps = NULL;
6240 /* accumulate trackpoint copies in newtps, in reverse order */
6241 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6243 if (count >= points) {
6244 /* flush accumulated trackpoints into new list */
6245 newlists = g_list_append(newlists, g_list_reverse(newtps));
6249 iter = g_list_next(iter);
6252 // If there is a remaining chunk put that into the new split list
6253 // This may well be the whole track if no split points were encountered
6255 newlists = g_list_append(newlists, g_list_reverse(newtps));
6258 /* put lists of trackpoints into tracks */
6260 // Only bother updating if the split results in new tracks
6261 if (g_list_length (newlists) > 1) {
6266 tr = vik_track_copy ( track, FALSE );
6267 tr->trackpoints = (GList *)(iter->data);
6269 if ( track->is_route ) {
6270 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6271 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6274 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6275 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6277 g_free ( new_tr_name );
6278 vik_track_calculate_bounds ( tr );
6280 iter = g_list_next(iter);
6282 // Remove original track and then update the display
6283 if ( track->is_route )
6284 vik_trw_layer_delete_route (vtl, track);
6286 vik_trw_layer_delete_track (vtl, track);
6287 vik_layer_emit_update(VIK_LAYER(vtl));
6289 g_list_free(newlists);
6293 * Split a track at the currently selected trackpoint
6295 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6297 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6298 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6299 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6303 * Split a track by its segments
6304 * Routes do not have segments so don't call this for routes
6306 static void trw_layer_split_segments ( menu_array_sublayer values )
6308 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6309 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6316 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6319 for ( i = 0; i < ntracks; i++ ) {
6321 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6322 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6323 g_free ( new_tr_name );
6328 // Remove original track
6329 vik_trw_layer_delete_track ( vtl, trk );
6330 vik_layer_emit_update ( VIK_LAYER(vtl) );
6333 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6336 /* end of split/merge routines */
6338 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6342 // Find available adjacent trackpoint
6343 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6344 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6345 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6347 // Delete current trackpoint
6348 vik_trackpoint_free ( vtl->current_tpl->data );
6349 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6351 // Set to current to the available adjacent trackpoint
6352 vtl->current_tpl = new_tpl;
6354 if ( vtl->current_tp_track ) {
6355 vik_track_calculate_bounds ( vtl->current_tp_track );
6359 // Delete current trackpoint
6360 vik_trackpoint_free ( vtl->current_tpl->data );
6361 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6362 trw_layer_cancel_current_tp ( vtl, FALSE );
6367 * Delete the selected point
6369 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6371 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6373 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6374 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6376 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6381 if ( !vtl->current_tpl )
6384 trw_layer_trackpoint_selected_delete ( vtl, trk );
6386 // Track has been updated so update tps:
6387 trw_layer_cancel_tps_of_track ( vtl, trk );
6389 vik_layer_emit_update ( VIK_LAYER(vtl) );
6393 * Delete adjacent track points at the same position
6394 * AKA Delete Dulplicates on the Properties Window
6396 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6398 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6400 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6401 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6403 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6408 gulong removed = vik_track_remove_dup_points ( trk );
6410 // Track has been updated so update tps:
6411 trw_layer_cancel_tps_of_track ( vtl, trk );
6413 // Inform user how much was deleted as it's not obvious from the normal view
6415 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6416 g_snprintf(str, 64, tmp_str, removed);
6417 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6419 vik_layer_emit_update ( VIK_LAYER(vtl) );
6423 * Delete adjacent track points with the same timestamp
6424 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6426 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6428 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6430 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6431 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6433 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6438 gulong removed = vik_track_remove_same_time_points ( trk );
6440 // Track has been updated so update tps:
6441 trw_layer_cancel_tps_of_track ( vtl, trk );
6443 // Inform user how much was deleted as it's not obvious from the normal view
6445 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6446 g_snprintf(str, 64, tmp_str, removed);
6447 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6449 vik_layer_emit_update ( VIK_LAYER(vtl) );
6455 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6457 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6459 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6460 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6462 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6467 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6469 vik_layer_emit_update ( VIK_LAYER(vtl) );
6472 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6474 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6476 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6477 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6479 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6484 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6486 vik_layer_emit_update ( VIK_LAYER(vtl) );
6492 static void trw_layer_reverse ( menu_array_sublayer values )
6494 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6496 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6497 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6499 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6504 vik_track_reverse ( track );
6506 vik_layer_emit_update ( VIK_LAYER(vtl) );
6510 * Similar to trw_layer_enum_item, but this uses a sorted method
6513 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6515 GList **list = (GList**)udata;
6516 // *list = g_list_prepend(*all, key); //unsorted method
6517 // Sort named list alphabetically
6518 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6523 * Now Waypoint specific sort
6525 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6527 GList **list = (GList**)udata;
6528 // Sort named list alphabetically
6529 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6533 * Track specific sort
6535 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6537 GList **list = (GList**)udata;
6538 // Sort named list alphabetically
6539 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6544 gboolean has_same_track_name;
6545 const gchar *same_track_name;
6546 } same_track_name_udata;
6548 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6550 const gchar* namea = (const gchar*) aa;
6551 const gchar* nameb = (const gchar*) bb;
6554 gint result = strcmp ( namea, nameb );
6556 if ( result == 0 ) {
6557 // Found two names the same
6558 same_track_name_udata *user_data = udata;
6559 user_data->has_same_track_name = TRUE;
6560 user_data->same_track_name = namea;
6563 // Leave ordering the same
6568 * Find out if any tracks have the same name in this hash table
6570 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6572 // Sort items by name, then compare if any next to each other are the same
6574 GList *track_names = NULL;
6575 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6578 if ( ! track_names )
6581 same_track_name_udata udata;
6582 udata.has_same_track_name = FALSE;
6584 // Use sort routine to traverse list comparing items
6585 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6586 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6587 // Still no tracks...
6591 return udata.has_same_track_name;
6595 * Force unqiue track names for the track table specified
6596 * Note the panel is a required parameter to enable the update of the names displayed
6597 * Specify if on tracks or else on routes
6599 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6601 // . Search list for an instance of repeated name
6602 // . get track of this name
6603 // . create new name
6604 // . rename track & update equiv. treeview iter
6605 // . repeat until all different
6607 same_track_name_udata udata;
6609 GList *track_names = NULL;
6610 udata.has_same_track_name = FALSE;
6611 udata.same_track_name = NULL;
6613 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6616 if ( ! track_names )
6619 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6621 // Still no tracks...
6622 if ( ! dummy_list1 )
6625 while ( udata.has_same_track_name ) {
6627 // Find a track with the same name
6630 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6632 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6636 g_critical("Houston, we've had a problem.");
6637 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6638 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6643 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6644 vik_track_set_name ( trk, newname );
6650 // Need want key of it for treeview update
6651 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6653 if ( trkf && udataU.uuid ) {
6657 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6659 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6662 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6664 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6666 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6670 // Start trying to find same names again...
6672 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6673 udata.has_same_track_name = FALSE;
6674 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6676 // No tracks any more - give up searching
6677 if ( ! dummy_list2 )
6678 udata.has_same_track_name = FALSE;
6682 vik_layers_panel_emit_update ( vlp );
6685 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6687 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6690 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6691 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6692 iter = &(vtl->tracks_iter);
6693 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6695 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6696 iter = &(vtl->routes_iter);
6697 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6699 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6700 iter = &(vtl->waypoints_iter);
6701 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6705 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6708 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6710 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6713 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6714 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6715 iter = &(vtl->tracks_iter);
6716 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6718 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6719 iter = &(vtl->routes_iter);
6720 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6722 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6723 iter = &(vtl->waypoints_iter);
6724 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6728 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6734 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6736 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6739 // Ensure list of track names offered is unique
6740 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6741 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6742 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6743 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6749 // Sort list alphabetically for better presentation
6750 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6753 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6757 // Get list of items to delete from the user
6758 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6761 _("Delete Selection"),
6762 _("Select tracks to delete"));
6765 // Delete requested tracks
6766 // since specificly requested, IMHO no need for extra confirmation
6767 if ( delete_list ) {
6769 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6770 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6771 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6773 g_list_free(delete_list);
6774 vik_layer_emit_update( VIK_LAYER(vtl) );
6781 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6783 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6786 // Ensure list of track names offered is unique
6787 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6788 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6789 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6790 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6796 // Sort list alphabetically for better presentation
6797 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6800 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6804 // Get list of items to delete from the user
6805 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6808 _("Delete Selection"),
6809 _("Select routes to delete") );
6812 // Delete requested routes
6813 // since specificly requested, IMHO no need for extra confirmation
6814 if ( delete_list ) {
6816 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6817 // This deletes first route it finds of that name (but uniqueness is enforced above)
6818 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6820 g_list_free(delete_list);
6821 vik_layer_emit_update( VIK_LAYER(vtl) );
6826 gboolean has_same_waypoint_name;
6827 const gchar *same_waypoint_name;
6828 } same_waypoint_name_udata;
6830 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6832 const gchar* namea = (const gchar*) aa;
6833 const gchar* nameb = (const gchar*) bb;
6836 gint result = strcmp ( namea, nameb );
6838 if ( result == 0 ) {
6839 // Found two names the same
6840 same_waypoint_name_udata *user_data = udata;
6841 user_data->has_same_waypoint_name = TRUE;
6842 user_data->same_waypoint_name = namea;
6845 // Leave ordering the same
6850 * Find out if any waypoints have the same name in this layer
6852 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6854 // Sort items by name, then compare if any next to each other are the same
6856 GList *waypoint_names = NULL;
6857 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6860 if ( ! waypoint_names )
6863 same_waypoint_name_udata udata;
6864 udata.has_same_waypoint_name = FALSE;
6866 // Use sort routine to traverse list comparing items
6867 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6868 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6869 // Still no waypoints...
6873 return udata.has_same_waypoint_name;
6877 * Force unqiue waypoint names for this layer
6878 * Note the panel is a required parameter to enable the update of the names displayed
6880 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6882 // . Search list for an instance of repeated name
6883 // . get waypoint of this name
6884 // . create new name
6885 // . rename waypoint & update equiv. treeview iter
6886 // . repeat until all different
6888 same_waypoint_name_udata udata;
6890 GList *waypoint_names = NULL;
6891 udata.has_same_waypoint_name = FALSE;
6892 udata.same_waypoint_name = NULL;
6894 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6897 if ( ! waypoint_names )
6900 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6902 // Still no waypoints...
6903 if ( ! dummy_list1 )
6906 while ( udata.has_same_waypoint_name ) {
6908 // Find a waypoint with the same name
6909 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6913 g_critical("Houston, we've had a problem.");
6914 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6915 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6920 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6922 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6924 // Start trying to find same names again...
6925 waypoint_names = NULL;
6926 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6927 udata.has_same_waypoint_name = FALSE;
6928 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6930 // No waypoints any more - give up searching
6931 if ( ! dummy_list2 )
6932 udata.has_same_waypoint_name = FALSE;
6936 vik_layers_panel_emit_update ( vlp );
6942 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6944 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6947 // Ensure list of waypoint names offered is unique
6948 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6949 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6950 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6951 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6957 // Sort list alphabetically for better presentation
6958 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6960 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6964 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6966 // Get list of items to delete from the user
6967 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6970 _("Delete Selection"),
6971 _("Select waypoints to delete"));
6974 // Delete requested waypoints
6975 // since specificly requested, IMHO no need for extra confirmation
6976 if ( delete_list ) {
6978 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6979 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6980 trw_layer_delete_waypoint_by_name (vtl, l->data);
6982 g_list_free(delete_list);
6984 trw_layer_calculate_bounds_waypoints ( vtl );
6985 vik_layer_emit_update( VIK_LAYER(vtl) );
6993 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6995 vik_treeview_item_toggle_visible ( vt, it );
7001 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7003 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7009 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7011 wp->visible = GPOINTER_TO_INT (on_off);
7017 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7019 wp->visible = !wp->visible;
7025 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7027 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7028 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7029 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7030 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7032 vik_layer_emit_update ( VIK_LAYER(vtl) );
7038 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7040 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7041 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7042 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7043 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7045 vik_layer_emit_update ( VIK_LAYER(vtl) );
7051 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7053 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7054 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7055 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7057 vik_layer_emit_update ( VIK_LAYER(vtl) );
7063 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7065 trk->visible = GPOINTER_TO_INT (on_off);
7071 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7073 trk->visible = !trk->visible;
7079 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7081 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7082 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7083 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7084 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7086 vik_layer_emit_update ( VIK_LAYER(vtl) );
7092 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7094 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7095 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7096 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7097 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7099 vik_layer_emit_update ( VIK_LAYER(vtl) );
7105 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7107 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7108 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7109 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7111 vik_layer_emit_update ( VIK_LAYER(vtl) );
7117 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7119 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7120 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7121 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7122 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7124 vik_layer_emit_update ( VIK_LAYER(vtl) );
7130 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7132 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7133 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7134 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7135 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7137 vik_layer_emit_update ( VIK_LAYER(vtl) );
7143 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7145 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7146 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7147 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7149 vik_layer_emit_update ( VIK_LAYER(vtl) );
7153 * vik_trw_layer_build_waypoint_list_t:
7155 * Helper function to construct a list of #vik_trw_waypoint_list_t
7157 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7159 GList *waypoints_and_layers = NULL;
7160 // build waypoints_and_layers list
7161 while ( waypoints ) {
7162 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7163 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7165 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7166 waypoints = g_list_next ( waypoints );
7168 return waypoints_and_layers;
7172 * trw_layer_create_waypoint_list:
7174 * Create the latest list of waypoints with the associated layer(s)
7175 * Although this will always be from a single layer here
7177 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7179 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7180 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7182 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7186 * trw_layer_analyse_close:
7188 * Stuff to do on dialog closure
7190 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7192 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7193 gtk_widget_destroy ( dialog );
7194 vtl->tracks_analysis_dialog = NULL;
7198 * vik_trw_layer_build_track_list_t:
7200 * Helper function to construct a list of #vik_trw_track_list_t
7202 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7204 GList *tracks_and_layers = NULL;
7205 // build tracks_and_layers list
7207 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7208 vtdl->trk = VIK_TRACK(tracks->data);
7210 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7211 tracks = g_list_next ( tracks );
7213 return tracks_and_layers;
7217 * trw_layer_create_track_list:
7219 * Create the latest list of tracks with the associated layer(s)
7220 * Although this will always be from a single layer here
7222 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7224 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7225 GList *tracks = NULL;
7226 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7227 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7229 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7231 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7234 static void trw_layer_tracks_stats ( menu_array_layer values )
7236 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7237 // There can only be one!
7238 if ( vtl->tracks_analysis_dialog )
7241 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7242 VIK_LAYER(vtl)->name,
7244 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7245 trw_layer_create_track_list,
7246 trw_layer_analyse_close );
7252 static void trw_layer_routes_stats ( menu_array_layer values )
7254 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7255 // There can only be one!
7256 if ( vtl->tracks_analysis_dialog )
7259 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7260 VIK_LAYER(vtl)->name,
7262 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7263 trw_layer_create_track_list,
7264 trw_layer_analyse_close );
7267 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7269 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7270 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7272 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7275 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7277 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7278 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7281 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7282 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7286 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7288 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7289 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7292 if ( !strncmp(wp->comment, "http", 4) ) {
7293 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7294 } else if ( !strncmp(wp->description, "http", 4) ) {
7295 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7299 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7301 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7303 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7305 // No actual change to the name supplied
7307 if (strcmp(newname, wp->name) == 0 )
7310 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7313 // An existing waypoint has been found with the requested name
7314 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7315 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7320 // Update WP name and refresh the treeview
7321 vik_waypoint_set_name (wp, newname);
7323 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7324 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7326 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7331 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7333 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7335 // No actual change to the name supplied
7337 if (strcmp(newname, trk->name) == 0)
7340 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7343 // An existing track has been found with the requested name
7344 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7345 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7349 // Update track name and refresh GUI parts
7350 vik_track_set_name (trk, newname);
7352 // Update any subwindows that could be displaying this track which has changed name
7353 // Only one Track Edit Window
7354 if ( l->current_tp_track == trk && l->tpwin ) {
7355 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7357 // Property Dialog of the track
7358 vik_trw_layer_propwin_update ( trk );
7360 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7361 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7363 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7368 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7370 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7372 // No actual change to the name supplied
7374 if (strcmp(newname, trk->name) == 0)
7377 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7380 // An existing track has been found with the requested name
7381 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7382 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7386 // Update track name and refresh GUI parts
7387 vik_track_set_name (trk, newname);
7389 // Update any subwindows that could be displaying this track which has changed name
7390 // Only one Track Edit Window
7391 if ( l->current_tp_track == trk && l->tpwin ) {
7392 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7394 // Property Dialog of the track
7395 vik_trw_layer_propwin_update ( trk );
7397 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7398 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7400 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7407 static gboolean is_valid_geocache_name ( gchar *str )
7409 gint len = strlen ( str );
7410 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]));
7413 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7415 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7416 a_acquire_set_filter_track ( trk );
7419 #ifdef VIK_CONFIG_GOOGLE
7420 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7422 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7423 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7426 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7428 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7430 gchar *escaped = uri_escape ( tr->comment );
7431 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7432 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7439 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7440 /* viewpoint is now available instead */
7441 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7443 static menu_array_sublayer pass_along;
7445 gboolean rv = FALSE;
7447 pass_along[MA_VTL] = l;
7448 pass_along[MA_VLP] = vlp;
7449 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7450 pass_along[MA_SUBLAYER_ID] = sublayer;
7451 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7452 pass_along[MA_VVP] = vvp;
7453 pass_along[MA_TV_ITER] = iter;
7454 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7456 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7460 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7462 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7463 gtk_widget_show ( item );
7465 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7466 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7467 if (tr && tr->property_dialog)
7468 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7470 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7471 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7472 if (tr && tr->property_dialog)
7473 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7476 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7477 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7478 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7479 gtk_widget_show ( item );
7481 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7483 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7484 gtk_widget_show ( item );
7486 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7488 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7489 gtk_widget_show ( item );
7491 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7493 // Always create separator as now there is always at least the transform menu option
7494 item = gtk_menu_item_new ();
7495 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7496 gtk_widget_show ( item );
7498 /* could be a right-click using the tool */
7499 if ( vlp != NULL ) {
7500 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7501 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7502 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7503 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7504 gtk_widget_show ( item );
7507 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7509 if ( wp && wp->name ) {
7510 if ( is_valid_geocache_name ( wp->name ) ) {
7511 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7513 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7514 gtk_widget_show ( item );
7516 #ifdef VIK_CONFIG_GEOTAG
7517 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7519 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7520 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7521 gtk_widget_show ( item );
7525 if ( wp && wp->image )
7527 // Set up image paramater
7528 pass_along[MA_MISC] = wp->image;
7530 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7531 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
7532 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7533 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7534 gtk_widget_show ( item );
7536 #ifdef VIK_CONFIG_GEOTAG
7537 GtkWidget *geotag_submenu = gtk_menu_new ();
7538 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7539 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7540 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7541 gtk_widget_show ( item );
7542 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7544 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7546 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7547 gtk_widget_show ( item );
7549 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7550 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7551 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7552 gtk_widget_show ( item );
7558 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7559 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7560 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7561 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7562 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7563 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7564 gtk_widget_show ( item );
7570 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7571 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7572 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7573 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7574 gtk_widget_show ( item );
7575 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7576 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7577 gtk_widget_set_sensitive ( item, TRUE );
7579 gtk_widget_set_sensitive ( item, FALSE );
7582 item = gtk_menu_item_new ();
7583 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7584 gtk_widget_show ( item );
7587 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7590 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7591 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7592 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7593 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7594 gtk_widget_show ( item );
7597 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7599 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7600 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7601 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7602 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7603 gtk_widget_show ( item );
7605 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7606 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7607 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7608 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7609 gtk_widget_show ( item );
7611 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7612 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7614 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7615 gtk_widget_show ( item );
7617 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7618 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7621 gtk_widget_show ( item );
7623 GtkWidget *vis_submenu = gtk_menu_new ();
7624 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7625 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7626 gtk_widget_show ( item );
7627 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7629 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7630 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7631 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7632 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7633 gtk_widget_show ( item );
7635 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7636 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7638 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7639 gtk_widget_show ( item );
7641 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7642 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7643 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7644 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7645 gtk_widget_show ( item );
7647 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7648 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7649 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7650 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7653 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7657 if ( l->current_track && !l->current_track->is_route ) {
7658 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7659 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7660 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7661 gtk_widget_show ( item );
7663 item = gtk_menu_item_new ();
7664 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7665 gtk_widget_show ( item );
7668 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7669 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7670 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7671 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7672 gtk_widget_show ( item );
7674 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7675 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7676 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7677 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7678 gtk_widget_show ( item );
7679 // Make it available only when a new track *not* already in progress
7680 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7682 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7683 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7684 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7685 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7686 gtk_widget_show ( item );
7688 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7689 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7691 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7692 gtk_widget_show ( item );
7694 GtkWidget *vis_submenu = gtk_menu_new ();
7695 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7696 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7697 gtk_widget_show ( item );
7698 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7700 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7701 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7703 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7704 gtk_widget_show ( item );
7706 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7707 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7708 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7709 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7710 gtk_widget_show ( item );
7712 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7713 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7714 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7715 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7717 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7719 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7720 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7721 gtk_widget_show ( item );
7723 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7724 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7725 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7726 gtk_widget_show ( item );
7729 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7733 if ( l->current_track && l->current_track->is_route ) {
7734 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7735 // Reuse finish track method
7736 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7737 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7738 gtk_widget_show ( item );
7740 item = gtk_menu_item_new ();
7741 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7742 gtk_widget_show ( item );
7745 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7746 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7748 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7749 gtk_widget_show ( item );
7751 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7752 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7754 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7755 gtk_widget_show ( item );
7756 // Make it available only when a new track *not* already in progress
7757 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7759 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7761 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7762 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7763 gtk_widget_show ( item );
7765 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7766 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7767 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7768 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7769 gtk_widget_show ( item );
7771 GtkWidget *vis_submenu = gtk_menu_new ();
7772 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7773 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7774 gtk_widget_show ( item );
7775 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7777 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7778 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7780 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7781 gtk_widget_show ( item );
7783 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7784 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7786 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7787 gtk_widget_show ( item );
7789 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7790 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7791 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7792 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7794 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7797 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7799 gtk_widget_show ( item );
7801 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7803 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7804 gtk_widget_show ( item );
7808 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7809 GtkWidget *submenu_sort = gtk_menu_new ();
7810 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7811 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7812 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7813 gtk_widget_show ( item );
7814 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7816 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7820 gtk_widget_show ( item );
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7825 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7826 gtk_widget_show ( item );
7829 GtkWidget *upload_submenu = gtk_menu_new ();
7831 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7833 item = gtk_menu_item_new ();
7834 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7835 gtk_widget_show ( item );
7837 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7838 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7839 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7840 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7841 if ( l->current_track ) {
7842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7843 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7844 gtk_widget_show ( item );
7847 item = gtk_menu_item_new ();
7848 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7849 gtk_widget_show ( item );
7852 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7855 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7856 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7857 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7858 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7859 gtk_widget_show ( item );
7861 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7863 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7864 gtk_widget_show ( item );
7866 GtkWidget *goto_submenu;
7867 goto_submenu = gtk_menu_new ();
7868 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7869 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7870 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7871 gtk_widget_show ( item );
7872 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7874 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7877 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7878 gtk_widget_show ( item );
7880 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7883 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7884 gtk_widget_show ( item );
7886 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7887 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7889 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7890 gtk_widget_show ( item );
7892 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7893 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7894 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7895 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7896 gtk_widget_show ( item );
7898 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7899 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7900 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7901 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7902 gtk_widget_show ( item );
7904 // Routes don't have speeds
7905 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7906 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7907 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7909 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7910 gtk_widget_show ( item );
7913 GtkWidget *combine_submenu;
7914 combine_submenu = gtk_menu_new ();
7915 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7916 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7917 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7918 gtk_widget_show ( item );
7919 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7921 // Routes don't have times or segments...
7922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7923 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7925 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7926 gtk_widget_show ( item );
7928 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7930 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7931 gtk_widget_show ( item );
7934 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7936 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7937 gtk_widget_show ( item );
7939 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7940 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7942 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7944 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7945 gtk_widget_show ( item );
7947 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7948 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7950 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7952 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7953 gtk_widget_show ( item );
7955 GtkWidget *split_submenu;
7956 split_submenu = gtk_menu_new ();
7957 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7958 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7959 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7960 gtk_widget_show ( item );
7961 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7963 // Routes don't have times or segments...
7964 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7965 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7966 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7967 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7968 gtk_widget_show ( item );
7970 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7971 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7973 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7974 gtk_widget_show ( item );
7977 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7979 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7980 gtk_widget_show ( item );
7982 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7984 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7985 gtk_widget_show ( item );
7986 // Make it available only when a trackpoint is selected.
7987 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7989 GtkWidget *insert_submenu = gtk_menu_new ();
7990 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7991 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7992 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7993 gtk_widget_show ( item );
7994 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7996 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7998 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7999 gtk_widget_show ( item );
8000 // Make it available only when a point is selected
8001 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8003 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8005 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8006 gtk_widget_show ( item );
8007 // Make it available only when a point is selected
8008 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8010 GtkWidget *delete_submenu;
8011 delete_submenu = gtk_menu_new ();
8012 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8013 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8014 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8015 gtk_widget_show ( item );
8016 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8018 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8019 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8021 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8022 gtk_widget_show ( item );
8023 // Make it available only when a point is selected
8024 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8026 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8028 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8029 gtk_widget_show ( item );
8031 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8033 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8034 gtk_widget_show ( item );
8036 GtkWidget *transform_submenu;
8037 transform_submenu = gtk_menu_new ();
8038 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8040 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8041 gtk_widget_show ( item );
8042 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8044 GtkWidget *dem_submenu;
8045 dem_submenu = gtk_menu_new ();
8046 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8047 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
8048 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8049 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8051 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8053 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8054 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8055 gtk_widget_show ( item );
8057 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8059 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8060 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8061 gtk_widget_show ( item );
8063 GtkWidget *smooth_submenu;
8064 smooth_submenu = gtk_menu_new ();
8065 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8066 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8067 gtk_widget_show ( item );
8068 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8070 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8072 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8073 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8074 gtk_widget_show ( item );
8076 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8078 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8079 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8080 gtk_widget_show ( item );
8082 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8083 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8085 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8086 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8087 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8088 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8089 gtk_widget_show ( item );
8091 // Routes don't have timestamps - so this is only available for tracks
8092 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8093 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8095 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8096 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8097 gtk_widget_show ( item );
8100 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8101 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8106 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8107 gtk_widget_show ( item );
8109 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8110 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8111 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8113 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8114 gtk_widget_show ( item );
8117 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8119 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8120 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8122 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8123 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
8124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8125 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8126 gtk_widget_show ( item );
8129 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8130 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8132 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8133 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8135 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8136 gtk_widget_show ( item );
8138 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8139 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8141 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8142 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8144 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8145 gtk_widget_show ( item );
8147 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8148 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8149 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
8150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8151 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8152 gtk_widget_show ( item );
8155 // ATM can't upload a single waypoint but can do waypoints to a GPS
8156 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8157 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8158 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8159 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8160 gtk_widget_show ( item );
8161 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8163 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8164 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8166 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8167 gtk_widget_show ( item );
8171 #ifdef VIK_CONFIG_GOOGLE
8172 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8174 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8177 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8178 gtk_widget_show ( item );
8182 // Some things aren't usable with routes
8183 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8184 #ifdef VIK_CONFIG_OPENSTREETMAP
8185 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8186 // Convert internal pointer into track
8187 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8188 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8190 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8191 gtk_widget_show ( item );
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8197 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8198 gtk_widget_show ( item );
8200 /* ATM This function is only available via the layers panel, due to needing a vlp */
8202 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8203 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8204 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8206 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8207 gtk_widget_show ( item );
8211 #ifdef VIK_CONFIG_GEOTAG
8212 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8214 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8215 gtk_widget_show ( item );
8219 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8220 // Only show on viewport popmenu when a trackpoint is selected
8221 if ( ! vlp && l->current_tpl ) {
8223 item = gtk_menu_item_new ();
8224 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8225 gtk_widget_show ( item );
8227 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8229 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8230 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8231 gtk_widget_show ( item );
8235 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8236 GtkWidget *transform_submenu;
8237 transform_submenu = gtk_menu_new ();
8238 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8239 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8240 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8241 gtk_widget_show ( item );
8242 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8244 GtkWidget *dem_submenu;
8245 dem_submenu = gtk_menu_new ();
8246 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8247 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
8248 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8249 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8251 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8253 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8254 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8255 gtk_widget_show ( item );
8257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8259 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8260 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8261 gtk_widget_show ( item );
8264 gtk_widget_show_all ( GTK_WIDGET(menu) );
8269 // TODO: Probably better to rework this track manipulation in viktrack.c
8270 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8273 if (!vtl->current_tpl)
8276 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8277 VikTrackpoint *tp_other = NULL;
8280 if (!vtl->current_tpl->prev)
8282 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8284 if (!vtl->current_tpl->next)
8286 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8289 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8292 VikTrackpoint *tp_new = vik_trackpoint_new();
8293 struct LatLon ll_current, ll_other;
8294 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8295 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8297 /* main positional interpolation */
8298 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8299 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8301 /* Now other properties that can be interpolated */
8302 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8304 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8305 /* Note here the division is applied to each part, then added
8306 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8307 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8308 tp_new->has_timestamp = TRUE;
8311 if (tp_current->speed != NAN && tp_other->speed != NAN)
8312 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8314 /* TODO - improve interpolation of course, as it may not be correct.
8315 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8316 [similar applies if value is in radians] */
8317 if (tp_current->course != NAN && tp_other->course != NAN)
8318 tp_new->course = (tp_current->course + tp_other->course)/2;
8320 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8322 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8323 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8325 // Otherwise try routes
8326 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8330 gint index = g_list_index ( trk->trackpoints, tp_current );
8334 // NB no recalculation of bounds since it is inserted between points
8335 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8340 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8346 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8350 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8352 if ( vtl->current_tpl )
8354 vtl->current_tpl = NULL;
8355 vtl->current_tp_track = NULL;
8356 vtl->current_tp_id = NULL;
8357 vik_layer_emit_update(VIK_LAYER(vtl));
8361 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8363 g_assert ( vtl->tpwin != NULL );
8364 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8365 trw_layer_cancel_current_tp ( vtl, TRUE );
8367 if ( vtl->current_tpl == NULL )
8370 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8372 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8373 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8375 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8377 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8379 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8383 trw_layer_trackpoint_selected_delete ( vtl, tr );
8385 if ( vtl->current_tpl )
8386 // Reset dialog with the available adjacent trackpoint
8387 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8389 vik_layer_emit_update(VIK_LAYER(vtl));
8391 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8393 if ( vtl->current_tp_track )
8394 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8395 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8397 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8399 if ( vtl->current_tp_track )
8400 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8401 vik_layer_emit_update(VIK_LAYER(vtl));
8403 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8405 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8406 vik_layer_emit_update(VIK_LAYER(vtl));
8408 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8409 vik_layer_emit_update(VIK_LAYER(vtl));
8413 * trw_layer_dialog_shift:
8414 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8416 * Try to reposition a dialog if it's over the specified coord
8417 * so to not obscure the item of interest
8419 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8421 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8423 // Attempt force dialog to be shown so we can find out where it is more reliably...
8424 while ( gtk_events_pending() )
8425 gtk_main_iteration ();
8427 // get parent window position & size
8428 gint win_pos_x, win_pos_y;
8429 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8431 gint win_size_x, win_size_y;
8432 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8434 // get own dialog size
8435 gint dia_size_x, dia_size_y;
8436 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8438 // get own dialog position
8439 gint dia_pos_x, dia_pos_y;
8440 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8442 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8443 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8445 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8447 gint vp_xx, vp_yy; // In viewport pixels
8448 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8450 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8454 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8456 // Transform Viewport pixels into absolute pixels
8457 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8458 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8460 // Is dialog over the point (to within an ^^ edge value)
8461 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8462 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8466 gint hh = vik_viewport_get_height ( vvp );
8468 // Consider the difference in viewport to the full window
8469 gint offset_y = dest_y;
8470 // Add difference between dialog and window sizes
8471 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8473 if ( vp_yy > hh/2 ) {
8474 // Point in bottom half, move window to top half
8475 gtk_window_move ( dialog, dia_pos_x, offset_y );
8478 // Point in top half, move dialog down
8479 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8483 // Shift left<->right
8484 gint ww = vik_viewport_get_width ( vvp );
8486 // Consider the difference in viewport to the full window
8487 gint offset_x = dest_x;
8488 // Add difference between dialog and window sizes
8489 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8491 if ( vp_xx > ww/2 ) {
8492 // Point on right, move window to left
8493 gtk_window_move ( dialog, offset_x, dia_pos_y );
8496 // Point on left, move right
8497 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8505 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8509 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8510 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8511 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8512 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8514 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8516 if ( vtl->current_tpl ) {
8517 // get tp pixel position
8518 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8520 // Shift up<->down to try not to obscure the trackpoint.
8521 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8525 if ( vtl->current_tpl )
8526 if ( vtl->current_tp_track )
8527 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8528 /* set layer name and TP data */
8531 /***************************************************************************
8533 ***************************************************************************/
8535 /*** Utility data structures and functions ****/
8539 gint closest_x, closest_y;
8540 gboolean draw_images;
8541 gpointer *closest_wp_id;
8542 VikWaypoint *closest_wp;
8548 gint closest_x, closest_y;
8549 gpointer closest_track_id;
8550 VikTrackpoint *closest_tp;
8556 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8562 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8564 // If waypoint has an image then use the image size to select
8565 if ( params->draw_images && wp->image ) {
8566 gint slackx, slacky;
8567 slackx = wp->image_width / 2;
8568 slacky = wp->image_height / 2;
8570 if ( x <= params->x + slackx && x >= params->x - slackx
8571 && y <= params->y + slacky && y >= params->y - slacky ) {
8572 params->closest_wp_id = id;
8573 params->closest_wp = wp;
8574 params->closest_x = x;
8575 params->closest_y = y;
8578 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8579 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8580 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8582 params->closest_wp_id = id;
8583 params->closest_wp = wp;
8584 params->closest_x = x;
8585 params->closest_y = y;
8589 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8591 GList *tpl = t->trackpoints;
8597 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8603 tp = VIK_TRACKPOINT(tpl->data);
8605 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8607 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8608 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8609 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8611 params->closest_track_id = id;
8612 params->closest_tp = tp;
8613 params->closest_tpl = tpl;
8614 params->closest_x = x;
8615 params->closest_y = y;
8621 // ATM: Leave this as 'Track' only.
8622 // Not overly bothered about having a snap to route trackpoint capability
8623 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8625 TPSearchParams params;
8629 params.closest_track_id = NULL;
8630 params.closest_tp = NULL;
8631 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8632 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8633 return params.closest_tp;
8636 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8638 WPSearchParams params;
8642 params.draw_images = vtl->drawimages;
8643 params.closest_wp = NULL;
8644 params.closest_wp_id = NULL;
8645 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8646 return params.closest_wp;
8650 // Some forward declarations
8651 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8652 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8653 static void marker_end_move ( tool_ed_t *t );
8656 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8660 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8662 // Here always allow snapping back to the original location
8663 // this is useful when one decides not to move the thing afterall
8664 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8667 if ( event->state & GDK_CONTROL_MASK )
8669 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8671 new_coord = tp->coord;
8675 if ( event->state & GDK_SHIFT_MASK )
8677 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8679 new_coord = wp->coord;
8683 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8685 marker_moveto ( t, x, y );
8692 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8694 if ( t->holding && event->button == 1 )
8697 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8700 if ( event->state & GDK_CONTROL_MASK )
8702 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8704 new_coord = tp->coord;
8708 if ( event->state & GDK_SHIFT_MASK )
8710 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8712 new_coord = wp->coord;
8715 marker_end_move ( t );
8717 // Determine if working on a waypoint or a trackpoint
8718 if ( t->is_waypoint ) {
8719 // Update waypoint position
8720 vtl->current_wp->coord = new_coord;
8721 trw_layer_calculate_bounds_waypoints ( vtl );
8722 // Reset waypoint pointer
8723 vtl->current_wp = NULL;
8724 vtl->current_wp_id = NULL;
8727 if ( vtl->current_tpl ) {
8728 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8730 if ( vtl->current_tp_track )
8731 vik_track_calculate_bounds ( vtl->current_tp_track );
8734 if ( vtl->current_tp_track )
8735 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8736 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8740 vik_layer_emit_update ( VIK_LAYER(vtl) );
8747 Returns true if a waypoint or track is found near the requested event position for this particular layer
8748 The item found is automatically selected
8749 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8751 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8753 if ( event->button != 1 )
8756 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8759 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8763 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8765 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8767 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8768 WPSearchParams wp_params;
8769 wp_params.vvp = vvp;
8770 wp_params.x = event->x;
8771 wp_params.y = event->y;
8772 wp_params.draw_images = vtl->drawimages;
8773 wp_params.closest_wp_id = NULL;
8774 wp_params.closest_wp = NULL;
8776 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8778 if ( wp_params.closest_wp ) {
8781 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8783 // Too easy to move it so must be holding shift to start immediately moving it
8784 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8785 if ( event->state & GDK_SHIFT_MASK ||
8786 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8787 // Put into 'move buffer'
8788 // NB vvp & vw already set in tet
8789 tet->vtl = (gpointer)vtl;
8790 tet->is_waypoint = TRUE;
8792 marker_begin_move (tet, event->x, event->y);
8795 vtl->current_wp = wp_params.closest_wp;
8796 vtl->current_wp_id = wp_params.closest_wp_id;
8798 if ( event->type == GDK_2BUTTON_PRESS ) {
8799 if ( vtl->current_wp->image ) {
8800 menu_array_sublayer values;
8801 values[MA_VTL] = vtl;
8802 values[MA_MISC] = vtl->current_wp->image;
8803 trw_layer_show_picture ( values );
8807 vik_layer_emit_update ( VIK_LAYER(vtl) );
8813 // Used for both track and route lists
8814 TPSearchParams tp_params;
8815 tp_params.vvp = vvp;
8816 tp_params.x = event->x;
8817 tp_params.y = event->y;
8818 tp_params.closest_track_id = NULL;
8819 tp_params.closest_tp = NULL;
8820 tp_params.closest_tpl = NULL;
8821 tp_params.bbox = bbox;
8823 if (vtl->tracks_visible) {
8824 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8826 if ( tp_params.closest_tp ) {
8828 // Always select + highlight the track
8829 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8831 tet->is_waypoint = FALSE;
8833 // Select the Trackpoint
8834 // Can move it immediately when control held or it's the previously selected tp
8835 if ( event->state & GDK_CONTROL_MASK ||
8836 vtl->current_tpl == tp_params.closest_tpl ) {
8837 // Put into 'move buffer'
8838 // NB vvp & vw already set in tet
8839 tet->vtl = (gpointer)vtl;
8840 marker_begin_move (tet, event->x, event->y);
8843 vtl->current_tpl = tp_params.closest_tpl;
8844 vtl->current_tp_id = tp_params.closest_track_id;
8845 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8847 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8850 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8852 vik_layer_emit_update ( VIK_LAYER(vtl) );
8857 // Try again for routes
8858 if (vtl->routes_visible) {
8859 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8861 if ( tp_params.closest_tp ) {
8863 // Always select + highlight the track
8864 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8866 tet->is_waypoint = FALSE;
8868 // Select the Trackpoint
8869 // Can move it immediately when control held or it's the previously selected tp
8870 if ( event->state & GDK_CONTROL_MASK ||
8871 vtl->current_tpl == tp_params.closest_tpl ) {
8872 // Put into 'move buffer'
8873 // NB vvp & vw already set in tet
8874 tet->vtl = (gpointer)vtl;
8875 marker_begin_move (tet, event->x, event->y);
8878 vtl->current_tpl = tp_params.closest_tpl;
8879 vtl->current_tp_id = tp_params.closest_track_id;
8880 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8882 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8885 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8887 vik_layer_emit_update ( VIK_LAYER(vtl) );
8892 /* these aren't the droids you're looking for */
8893 vtl->current_wp = NULL;
8894 vtl->current_wp_id = NULL;
8895 trw_layer_cancel_current_tp ( vtl, FALSE );
8898 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8903 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8905 if ( event->button != 3 )
8908 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8911 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8914 /* Post menu for the currently selected item */
8916 /* See if a track is selected */
8917 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8918 if ( track && track->visible ) {
8920 if ( track->name ) {
8922 if ( vtl->track_right_click_menu )
8923 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8925 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8932 if ( track->is_route )
8933 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8935 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8937 if ( trkf && udataU.uuid ) {
8940 if ( track->is_route )
8941 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8943 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8945 trw_layer_sublayer_add_menu_items ( vtl,
8946 vtl->track_right_click_menu,
8948 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8954 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8960 /* See if a waypoint is selected */
8961 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8962 if ( waypoint && waypoint->visible ) {
8963 if ( waypoint->name ) {
8965 if ( vtl->wp_right_click_menu )
8966 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8968 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8971 udata.wp = waypoint;
8974 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8976 if ( wpf && udata.uuid ) {
8977 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8979 trw_layer_sublayer_add_menu_items ( vtl,
8980 vtl->wp_right_click_menu,
8982 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8987 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8996 /* background drawing hook, to be passed the viewport */
8997 static gboolean tool_sync_done = TRUE;
8999 static gboolean tool_sync(gpointer data)
9001 VikViewport *vvp = data;
9002 gdk_threads_enter();
9003 vik_viewport_sync(vvp);
9004 tool_sync_done = TRUE;
9005 gdk_threads_leave();
9009 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9012 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9013 gdk_gc_set_function ( t->gc, GDK_INVERT );
9014 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9015 vik_viewport_sync(t->vvp);
9020 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9022 VikViewport *vvp = t->vvp;
9023 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9024 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9028 if (tool_sync_done) {
9029 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9030 tool_sync_done = FALSE;
9034 static void marker_end_move ( tool_ed_t *t )
9036 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9037 g_object_unref ( t->gc );
9041 /*** Edit waypoint ****/
9043 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9045 tool_ed_t *t = g_new(tool_ed_t, 1);
9051 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9056 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9058 WPSearchParams params;
9059 tool_ed_t *t = data;
9060 VikViewport *vvp = t->vvp;
9062 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9069 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9072 if ( vtl->current_wp && vtl->current_wp->visible )
9074 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9076 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9078 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9079 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9081 if ( event->button == 3 )
9082 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9084 marker_begin_move(t, event->x, event->y);
9091 params.x = event->x;
9092 params.y = event->y;
9093 params.draw_images = vtl->drawimages;
9094 params.closest_wp_id = NULL;
9095 params.closest_wp = NULL;
9096 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9097 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9099 marker_begin_move(t, event->x, event->y);
9102 else if ( params.closest_wp )
9104 if ( event->button == 3 )
9105 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9107 vtl->waypoint_rightclick = FALSE;
9109 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9111 vtl->current_wp = params.closest_wp;
9112 vtl->current_wp_id = params.closest_wp_id;
9114 /* could make it so don't update if old WP is off screen and new is null but oh well */
9115 vik_layer_emit_update ( VIK_LAYER(vtl) );
9119 vtl->current_wp = NULL;
9120 vtl->current_wp_id = NULL;
9121 vtl->waypoint_rightclick = FALSE;
9122 vik_layer_emit_update ( VIK_LAYER(vtl) );
9126 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9128 tool_ed_t *t = data;
9129 VikViewport *vvp = t->vvp;
9131 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9136 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9139 if ( event->state & GDK_CONTROL_MASK )
9141 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9143 new_coord = tp->coord;
9147 if ( event->state & GDK_SHIFT_MASK )
9149 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9150 if ( wp && wp != vtl->current_wp )
9151 new_coord = wp->coord;
9156 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9158 marker_moveto ( t, x, y );
9165 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9167 tool_ed_t *t = data;
9168 VikViewport *vvp = t->vvp;
9170 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9173 if ( t->holding && event->button == 1 )
9176 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9179 if ( event->state & GDK_CONTROL_MASK )
9181 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9183 new_coord = tp->coord;
9187 if ( event->state & GDK_SHIFT_MASK )
9189 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9190 if ( wp && wp != vtl->current_wp )
9191 new_coord = wp->coord;
9194 marker_end_move ( t );
9196 vtl->current_wp->coord = new_coord;
9198 trw_layer_calculate_bounds_waypoints ( vtl );
9199 vik_layer_emit_update ( VIK_LAYER(vtl) );
9202 /* PUT IN RIGHT PLACE!!! */
9203 if ( event->button == 3 && vtl->waypoint_rightclick )
9205 if ( vtl->wp_right_click_menu )
9206 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9207 if ( vtl->current_wp ) {
9208 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9209 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 );
9210 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9212 vtl->waypoint_rightclick = FALSE;
9217 /*** New track ****/
9219 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9226 GdkDrawable *drawable;
9232 * Draw specified pixmap
9234 static gboolean draw_sync ( gpointer data )
9236 draw_sync_t *ds = (draw_sync_t*) data;
9237 // Sometimes don't want to draw
9238 // normally because another update has taken precedent such as panning the display
9239 // which means this pixmap is no longer valid
9240 if ( ds->vtl->draw_sync_do ) {
9241 gdk_threads_enter();
9242 gdk_draw_drawable (ds->drawable,
9245 0, 0, 0, 0, -1, -1);
9246 ds->vtl->draw_sync_done = TRUE;
9247 gdk_threads_leave();
9253 static gchar* distance_string (gdouble distance)
9257 /* draw label with distance */
9258 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9259 switch (dist_units) {
9260 case VIK_UNITS_DISTANCE_MILES:
9261 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9262 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9263 } else if (distance < 1609.4) {
9264 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9266 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9270 // VIK_UNITS_DISTANCE_KILOMETRES
9271 if (distance >= 1000 && distance < 100000) {
9272 g_sprintf(str, "%3.2f km", distance/1000.0);
9273 } else if (distance < 1000) {
9274 g_sprintf(str, "%d m", (int)distance);
9276 g_sprintf(str, "%d km", (int)distance/1000);
9280 return g_strdup (str);
9284 * Actually set the message in statusbar
9286 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9288 // Only show elevation data when track has some elevation properties
9289 gchar str_gain_loss[64];
9290 str_gain_loss[0] = '\0';
9291 gchar str_last_step[64];
9292 str_last_step[0] = '\0';
9293 gchar *str_total = distance_string (distance);
9295 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9296 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9297 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9299 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9302 if ( last_step > 0 ) {
9303 gchar *tmp = distance_string (last_step);
9304 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9308 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9310 // Write with full gain/loss information
9311 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9312 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9314 g_free ( str_total );
9318 * Figure out what information should be set in the statusbar and then write it
9320 static void update_statusbar ( VikTrwLayer *vtl )
9322 // Get elevation data
9323 gdouble elev_gain, elev_loss;
9324 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9326 /* Find out actual distance of current track */
9327 gdouble distance = vik_track_get_length (vtl->current_track);
9329 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9333 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9335 /* if we haven't sync'ed yet, we don't have time to do more. */
9336 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9337 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9339 static GdkPixmap *pixmap = NULL;
9341 // Need to check in case window has been resized
9342 w1 = vik_viewport_get_width(vvp);
9343 h1 = vik_viewport_get_height(vvp);
9345 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9347 gdk_drawable_get_size (pixmap, &w2, &h2);
9348 if (w1 != w2 || h1 != h2) {
9349 g_object_unref ( G_OBJECT ( pixmap ) );
9350 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9353 // Reset to background
9354 gdk_draw_drawable (pixmap,
9355 vtl->current_track_newpoint_gc,
9356 vik_viewport_get_pixmap(vvp),
9357 0, 0, 0, 0, -1, -1);
9359 draw_sync_t *passalong;
9362 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9364 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9365 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9366 // thus when we come to reset to the background it would include what we have already drawn!!
9367 gdk_draw_line ( pixmap,
9368 vtl->current_track_newpoint_gc,
9369 x1, y1, event->x, event->y );
9370 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9372 /* Find out actual distance of current track */
9373 gdouble distance = vik_track_get_length (vtl->current_track);
9375 // Now add distance to where the pointer is //
9378 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9379 vik_coord_to_latlon ( &coord, &ll );
9380 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9381 distance = distance + last_step;
9383 // Get elevation data
9384 gdouble elev_gain, elev_loss;
9385 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9387 // Adjust elevation data (if available) for the current pointer position
9389 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9390 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9391 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9392 // Adjust elevation of last track point
9393 if ( elev_new > last_tpt->altitude )
9395 elev_gain += elev_new - last_tpt->altitude;
9398 elev_loss += last_tpt->altitude - elev_new;
9403 // Display of the distance 'tooltip' during track creation is controlled by a preference
9405 if ( a_vik_get_create_track_tooltip() ) {
9407 gchar *str = distance_string (distance);
9409 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9410 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9411 pango_layout_set_text (pl, str, -1);
9413 pango_layout_get_pixel_size ( pl, &wd, &hd );
9416 // offset from cursor a bit depending on font size
9420 // Create a background block to make the text easier to read over the background map
9421 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9422 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9423 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9425 g_object_unref ( G_OBJECT ( pl ) );
9426 g_object_unref ( G_OBJECT ( background_block_gc ) );
9430 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9431 passalong->vtl = vtl;
9432 passalong->pixmap = pixmap;
9433 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9434 passalong->gc = vtl->current_track_newpoint_gc;
9438 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9440 // Update statusbar with full gain/loss information
9441 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9443 // draw pixmap when we have time to
9444 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9445 vtl->draw_sync_done = FALSE;
9446 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9448 return VIK_LAYER_TOOL_ACK;
9451 // NB vtl->current_track must be valid
9452 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9455 if ( vtl->current_track->trackpoints ) {
9456 // TODO rework this...
9457 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9458 GList *last = g_list_last(vtl->current_track->trackpoints);
9459 g_free ( last->data );
9460 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9462 vik_track_calculate_bounds ( vtl->current_track );
9466 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9468 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9469 vtl->current_track = NULL;
9470 vik_layer_emit_update ( VIK_LAYER(vtl) );
9472 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9473 undo_trackpoint_add ( vtl );
9474 update_statusbar ( vtl );
9475 vik_layer_emit_update ( VIK_LAYER(vtl) );
9482 * Common function to handle trackpoint button requests on either a route or a track
9483 * . enables adding a point via normal click
9484 * . enables removal of last point via right click
9485 * . finishing of the track or route via double clicking
9487 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9491 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9494 if ( event->button == 2 ) {
9495 // As the display is panning, the new track pixmap is now invalid so don't draw it
9496 // otherwise this drawing done results in flickering back to an old image
9497 vtl->draw_sync_do = FALSE;
9501 if ( event->button == 3 )
9503 if ( !vtl->current_track )
9505 undo_trackpoint_add ( vtl );
9506 update_statusbar ( vtl );
9507 vik_layer_emit_update ( VIK_LAYER(vtl) );
9511 if ( event->type == GDK_2BUTTON_PRESS )
9513 /* subtract last (duplicate from double click) tp then end */
9514 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9516 /* undo last, then end */
9517 undo_trackpoint_add ( vtl );
9518 vtl->current_track = NULL;
9520 vik_layer_emit_update ( VIK_LAYER(vtl) );
9524 tp = vik_trackpoint_new();
9525 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9527 /* snap to other TP */
9528 if ( event->state & GDK_CONTROL_MASK )
9530 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9532 tp->coord = other_tp->coord;
9535 tp->newsegment = FALSE;
9536 tp->has_timestamp = FALSE;
9539 if ( vtl->current_track ) {
9540 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9541 /* Auto attempt to get elevation from DEM data (if it's available) */
9542 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9545 vtl->ct_x1 = vtl->ct_x2;
9546 vtl->ct_y1 = vtl->ct_y2;
9547 vtl->ct_x2 = event->x;
9548 vtl->ct_y2 = event->y;
9550 vik_layer_emit_update ( VIK_LAYER(vtl) );
9554 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9556 // ----------------------------------------------------- if current is a route - switch to new track
9557 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9559 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9560 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9562 new_track_create_common ( vtl, name );
9568 return tool_new_track_or_route_click ( vtl, event, vvp );
9571 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9573 if ( event->button == 2 ) {
9574 // Pan moving ended - enable potential point drawing again
9575 vtl->draw_sync_do = TRUE;
9576 vtl->draw_sync_done = TRUE;
9580 /*** New route ****/
9582 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9587 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9589 // -------------------------- if current is a track - switch to new route
9590 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9592 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9593 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9594 new_route_create_common ( vtl, name );
9600 return tool_new_track_or_route_click ( vtl, event, vvp );
9603 /*** New waypoint ****/
9605 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9610 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9613 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9615 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9616 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9617 trw_layer_calculate_bounds_waypoints ( vtl );
9618 vik_layer_emit_update ( VIK_LAYER(vtl) );
9624 /*** Edit trackpoint ****/
9626 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9628 tool_ed_t *t = g_new(tool_ed_t, 1);
9634 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9639 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9641 tool_ed_t *t = data;
9642 VikViewport *vvp = t->vvp;
9643 TPSearchParams params;
9644 /* OUTDATED DOCUMENTATION:
9645 find 5 pixel range on each side. then put these UTM, and a pointer
9646 to the winning track name (and maybe the winning track itself), and a
9647 pointer to the winning trackpoint, inside an array or struct. pass
9648 this along, do a foreach on the tracks which will do a foreach on the
9651 params.x = event->x;
9652 params.y = event->y;
9653 params.closest_track_id = NULL;
9654 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9655 params.closest_tp = NULL;
9656 params.closest_tpl = NULL;
9657 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9659 if ( event->button != 1 )
9662 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9665 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9668 if ( vtl->current_tpl )
9670 /* 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.) */
9671 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9672 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9677 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9679 if ( current_tr->visible &&
9680 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9681 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9682 marker_begin_move ( t, event->x, event->y );
9688 if ( vtl->tracks_visible )
9689 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9691 if ( params.closest_tp )
9693 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9694 vtl->current_tpl = params.closest_tpl;
9695 vtl->current_tp_id = params.closest_track_id;
9696 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9697 trw_layer_tpwin_init ( vtl );
9698 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9699 vik_layer_emit_update ( VIK_LAYER(vtl) );
9703 if ( vtl->routes_visible )
9704 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9706 if ( params.closest_tp )
9708 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9709 vtl->current_tpl = params.closest_tpl;
9710 vtl->current_tp_id = params.closest_track_id;
9711 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9712 trw_layer_tpwin_init ( vtl );
9713 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9714 vik_layer_emit_update ( VIK_LAYER(vtl) );
9718 /* these aren't the droids you're looking for */
9722 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9724 tool_ed_t *t = data;
9725 VikViewport *vvp = t->vvp;
9727 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9733 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9736 if ( event->state & GDK_CONTROL_MASK )
9738 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9739 if ( tp && tp != vtl->current_tpl->data )
9740 new_coord = tp->coord;
9742 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9745 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9746 marker_moveto ( t, x, y );
9754 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9756 tool_ed_t *t = data;
9757 VikViewport *vvp = t->vvp;
9759 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9761 if ( event->button != 1)
9766 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9769 if ( event->state & GDK_CONTROL_MASK )
9771 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9772 if ( tp && tp != vtl->current_tpl->data )
9773 new_coord = tp->coord;
9776 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9777 if ( vtl->current_tp_track )
9778 vik_track_calculate_bounds ( vtl->current_tp_track );
9780 marker_end_move ( t );
9782 /* diff dist is diff from orig */
9784 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9786 vik_layer_emit_update ( VIK_LAYER(vtl) );
9793 /*** Route Finder ***/
9794 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9799 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9802 if ( !vtl ) return FALSE;
9803 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9804 if ( event->button == 3 && vtl->route_finder_current_track ) {
9806 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9808 vtl->route_finder_coord = *new_end;
9810 vik_layer_emit_update ( VIK_LAYER(vtl) );
9811 /* remove last ' to:...' */
9812 if ( vtl->route_finder_current_track->comment ) {
9813 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9814 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9815 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9816 last_to - vtl->route_finder_current_track->comment - 1);
9817 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9822 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9823 struct LatLon start, end;
9825 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9826 vik_coord_to_latlon ( &(tmp), &end );
9827 vtl->route_finder_coord = tmp; /* for continuations */
9829 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9830 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9831 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9833 vtl->route_finder_check_added_track = TRUE;
9834 vtl->route_finder_started = FALSE;
9837 vik_routing_default_find ( vtl, start, end);
9839 /* see if anything was done -- a track was added or appended to */
9840 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9841 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 ) );
9842 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9843 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9844 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9845 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9848 if ( vtl->route_finder_added_track )
9849 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9851 vtl->route_finder_added_track = NULL;
9852 vtl->route_finder_check_added_track = FALSE;
9853 vtl->route_finder_append = FALSE;
9855 vik_layer_emit_update ( VIK_LAYER(vtl) );
9857 vtl->route_finder_started = TRUE;
9858 vtl->route_finder_coord = tmp;
9859 vtl->route_finder_current_track = NULL;
9864 /*** Show picture ****/
9866 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9871 /* Params are: vvp, event, last match found or NULL */
9872 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9874 if ( wp->image && wp->visible )
9876 gint x, y, slackx, slacky;
9877 GdkEventButton *event = (GdkEventButton *) params[1];
9879 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9880 slackx = wp->image_width / 2;
9881 slacky = wp->image_height / 2;
9882 if ( x <= event->x + slackx && x >= event->x - slackx
9883 && y <= event->y + slacky && y >= event->y - slacky )
9885 params[2] = wp->image; /* we've found a match. however continue searching
9886 * since we want to find the last match -- that
9887 * is, the match that was drawn last. */
9892 static void trw_layer_show_picture ( menu_array_sublayer values )
9894 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9896 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9899 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9900 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9901 g_free ( quoted_file );
9902 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9904 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() );
9905 g_error_free ( err );
9908 #endif /* WINDOWS */
9911 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9913 gpointer params[3] = { vvp, event, NULL };
9914 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9916 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9919 static menu_array_sublayer values;
9920 values[MA_VTL] = vtl;
9921 values[MA_MISC] = params[2];
9922 trw_layer_show_picture ( values );
9923 return TRUE; /* found a match */
9926 return FALSE; /* go through other layers, searching for a match */
9929 /***************************************************************************
9931 ***************************************************************************/
9934 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9936 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9937 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9940 /* Structure for thumbnail creating data used in the background thread */
9942 VikTrwLayer *vtl; // Layer needed for redrawing
9943 GSList *pics; // Image list
9944 } thumbnail_create_thread_data;
9946 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9948 guint total = g_slist_length(tctd->pics), done = 0;
9949 while ( tctd->pics )
9951 a_thumbnails_create ( (gchar *) tctd->pics->data );
9952 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9954 return -1; /* Abort thread */
9956 tctd->pics = tctd->pics->next;
9959 // Redraw to show the thumbnails as they are now created
9960 if ( IS_VIK_LAYER(tctd->vtl) )
9961 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9966 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9968 while ( tctd->pics )
9970 g_free ( tctd->pics->data );
9971 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9976 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9978 if ( ! vtl->has_verified_thumbnails )
9980 GSList *pics = NULL;
9981 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9984 gint len = g_slist_length ( pics );
9985 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9986 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9989 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9991 (vik_thr_func) create_thumbnails_thread,
9993 (vik_thr_free_func) thumbnail_create_thread_free,
10001 static const gchar* my_track_colors ( gint ii )
10003 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10015 // Fast and reliable way of returning a colour
10016 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10019 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10021 GHashTableIter iter;
10022 gpointer key, value;
10026 g_hash_table_iter_init ( &iter, vtl->tracks );
10028 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10030 // Tracks get a random spread of colours if not already assigned
10031 if ( ! VIK_TRACK(value)->has_color ) {
10032 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10033 VIK_TRACK(value)->color = vtl->track_color;
10035 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10037 VIK_TRACK(value)->has_color = TRUE;
10040 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10043 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10049 g_hash_table_iter_init ( &iter, vtl->routes );
10051 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10053 // Routes get an intermix of reds
10054 if ( ! VIK_TRACK(value)->has_color ) {
10056 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10058 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10059 VIK_TRACK(value)->has_color = TRUE;
10062 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10069 * (Re)Calculate the bounds of the waypoints in this layer,
10070 * This should be called whenever waypoints are changed
10072 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10074 struct LatLon topleft = { 0.0, 0.0 };
10075 struct LatLon bottomright = { 0.0, 0.0 };
10078 GHashTableIter iter;
10079 gpointer key, value;
10081 g_hash_table_iter_init ( &iter, vtl->waypoints );
10083 // Set bounds to first point
10084 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10085 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10086 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10089 // Ensure there is another point...
10090 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10092 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10094 // See if this point increases the bounds.
10095 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10097 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10098 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10099 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10100 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10104 vtl->waypoints_bbox.north = topleft.lat;
10105 vtl->waypoints_bbox.east = bottomright.lon;
10106 vtl->waypoints_bbox.south = bottomright.lat;
10107 vtl->waypoints_bbox.west = topleft.lon;
10110 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10112 vik_track_calculate_bounds ( trk );
10115 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10117 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10118 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10121 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10123 if ( ! VIK_LAYER(vtl)->vt )
10126 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10127 if ( g_hash_table_size (vtl->tracks) > 1 )
10128 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10130 if ( g_hash_table_size (vtl->routes) > 1 )
10131 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10133 if ( g_hash_table_size (vtl->waypoints) > 1 )
10134 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10137 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10139 if ( VIK_LAYER(vtl)->realized )
10140 trw_layer_verify_thumbnails ( vtl, vvp );
10141 trw_layer_track_alloc_colors ( vtl );
10143 trw_layer_calculate_bounds_waypoints ( vtl );
10144 trw_layer_calculate_bounds_tracks ( vtl );
10146 // Apply treeview sort after loading all the tracks for this layer
10147 // (rather than sorted insert on each individual track additional)
10148 // and after subsequent changes to the properties as the specified order may have changed.
10149 // since the sorting of a treeview section is now very quick
10150 // NB sorting is also performed after every name change as well to maintain the list order
10151 trw_layer_sort_all ( vtl );
10153 // Setting metadata time if not otherwise set
10154 if ( vtl->metadata ) {
10156 gboolean need_to_set_time = TRUE;
10157 if ( vtl->metadata->timestamp ) {
10158 need_to_set_time = FALSE;
10159 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10160 need_to_set_time = TRUE;
10163 if ( need_to_set_time ) {
10164 // Could rewrite this as a general get first time of a TRW Layer function
10165 GTimeVal timestamp;
10166 timestamp.tv_usec = 0;
10167 gboolean has_timestamp = FALSE;
10170 gl = g_hash_table_get_values ( vtl->tracks );
10171 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10172 gl = g_list_first ( gl );
10174 // Check times of tracks
10176 // Only need to check the first track as they have been sorted by time
10177 VikTrack *trk = (VikTrack*)gl->data;
10178 // Assume trackpoints already sorted by time
10179 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10180 if ( tpt && tpt->has_timestamp ) {
10181 timestamp.tv_sec = tpt->timestamp;
10182 has_timestamp = TRUE;
10184 g_list_free ( gl );
10187 if ( !has_timestamp ) {
10188 // 'Last' resort - current time
10189 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10190 g_get_current_time ( ×tamp );
10192 // Check times of waypoints
10193 gl = g_hash_table_get_values ( vtl->waypoints );
10195 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10196 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10197 if ( wpt->has_timestamp ) {
10198 if ( timestamp.tv_sec > wpt->timestamp ) {
10199 timestamp.tv_sec = wpt->timestamp;
10200 has_timestamp = TRUE;
10204 g_list_free ( gl );
10207 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10212 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10214 return vtl->coord_mode;
10218 * Uniquify the whole layer
10219 * Also requires the layers panel as the names shown there need updating too
10220 * Returns whether the operation was successful or not
10222 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10224 if ( vtl && vlp ) {
10225 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10226 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10227 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10233 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10235 vik_coord_convert ( &(wp->coord), *dest_mode );
10238 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10240 vik_track_convert ( tr, *dest_mode );
10243 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10245 if ( vtl->coord_mode != dest_mode )
10247 vtl->coord_mode = dest_mode;
10248 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10249 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10250 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10254 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10256 vtl->menu_selection = selection;
10259 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10261 return (vtl->menu_selection);
10264 /* ----------- Downloading maps along tracks --------------- */
10266 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10268 /* TODO: calculating based on current size of viewport */
10269 const gdouble w_at_zoom_0_125 = 0.0013;
10270 const gdouble h_at_zoom_0_125 = 0.0011;
10271 gdouble zoom_factor = zoom_level/0.125;
10273 wh->lat = h_at_zoom_0_125 * zoom_factor;
10274 wh->lon = w_at_zoom_0_125 * zoom_factor;
10276 return 0; /* all OK */
10279 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10281 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10282 (dist->lat >= ABS(to->north_south - from->north_south)))
10285 VikCoord *coord = g_malloc(sizeof(VikCoord));
10286 coord->mode = VIK_COORD_LATLON;
10288 if (ABS(gradient) < 1) {
10289 if (from->east_west > to->east_west)
10290 coord->east_west = from->east_west - dist->lon;
10292 coord->east_west = from->east_west + dist->lon;
10293 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10295 if (from->north_south > to->north_south)
10296 coord->north_south = from->north_south - dist->lat;
10298 coord->north_south = from->north_south + dist->lat;
10299 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10305 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10307 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10308 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10310 VikCoord *next = from;
10312 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10314 list = g_list_prepend(list, next);
10320 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10322 typedef struct _Rect {
10327 #define GLRECT(iter) ((Rect *)((iter)->data))
10330 GList *rects_to_download = NULL;
10333 if (get_download_area_width(vvp, zoom_level, &wh))
10336 GList *iter = tr->trackpoints;
10340 gboolean new_map = TRUE;
10341 VikCoord *cur_coord, tl, br;
10344 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10346 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10347 rect = g_malloc(sizeof(Rect));
10350 rect->center = *cur_coord;
10351 rects_to_download = g_list_prepend(rects_to_download, rect);
10356 gboolean found = FALSE;
10357 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10358 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10369 GList *fillins = NULL;
10370 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10371 /* seems that ATM the function get_next_coord works only for LATLON */
10372 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10373 /* fill-ins for far apart points */
10374 GList *cur_rect, *next_rect;
10375 for (cur_rect = rects_to_download;
10376 (next_rect = cur_rect->next) != NULL;
10377 cur_rect = cur_rect->next) {
10378 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10379 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10380 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10384 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10387 GList *iter = fillins;
10389 cur_coord = (VikCoord *)(iter->data);
10390 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10391 rect = g_malloc(sizeof(Rect));
10394 rect->center = *cur_coord;
10395 rects_to_download = g_list_prepend(rects_to_download, rect);
10400 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10401 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10405 for (iter = fillins; iter; iter = iter->next)
10406 g_free(iter->data);
10407 g_list_free(fillins);
10409 if (rects_to_download) {
10410 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10411 g_free(rect_iter->data);
10412 g_list_free(rects_to_download);
10416 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10420 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10421 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10422 gint selected_zoom, default_zoom;
10424 VikTrwLayer *vtl = values[MA_VTL];
10425 VikLayersPanel *vlp = values[MA_VLP];
10427 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10428 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10430 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10434 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10436 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10437 int num_maps = g_list_length(vmls);
10440 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10444 // Convert from list of vmls to list of names. Allowing the user to select one of them
10445 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10446 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10448 gchar **np = map_names;
10449 VikMapsLayer **lp = map_layers;
10451 for (i = 0; i < num_maps; i++) {
10452 vml = (VikMapsLayer *)(vmls->data);
10454 *np++ = vik_maps_layer_get_map_label(vml);
10457 // Mark end of the array lists
10461 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10462 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10463 if (cur_zoom == zoom_vals[default_zoom])
10466 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10468 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10471 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10474 for (i = 0; i < num_maps; i++)
10475 g_free(map_names[i]);
10477 g_free(map_layers);
10483 /**** lowest waypoint number calculation ***/
10484 static gint highest_wp_number_name_to_number(const gchar *name) {
10485 if ( strlen(name) == 3 ) {
10486 int n = atoi(name);
10487 if ( n < 100 && name[0] != '0' )
10489 if ( n < 10 && name[0] != '0' )
10497 static void highest_wp_number_reset(VikTrwLayer *vtl)
10499 vtl->highest_wp_number = -1;
10502 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10504 /* if is bigger that top, add it */
10505 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10506 if ( new_wp_num > vtl->highest_wp_number )
10507 vtl->highest_wp_number = new_wp_num;
10510 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10512 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10513 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10514 if ( vtl->highest_wp_number == old_wp_num ) {
10516 vtl->highest_wp_number--;
10518 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10519 /* search down until we find something that *does* exist */
10521 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10522 vtl->highest_wp_number--;
10523 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10528 /* get lowest unused number */
10529 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10532 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10534 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10535 return g_strdup(buf);
10539 * trw_layer_create_track_list_both:
10541 * Create the latest list of tracks and routes
10543 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10545 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10546 GList *tracks = NULL;
10547 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10548 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10550 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10553 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10555 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10557 gchar *title = NULL;
10558 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10559 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10561 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10563 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10567 static void trw_layer_track_list_dialog ( menu_array_layer values )
10569 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10571 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10572 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10576 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10578 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10580 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10581 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );