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), TRUE );
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 // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2373 // This may seem slightly inefficient to test each time for every layer
2374 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2375 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2376 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2378 trw_layer_draw_with_highlight ( l, data, FALSE );
2381 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2383 // Check the layer for visibility (including all the parents visibilities)
2384 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2386 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2390 * vik_trw_layer_draw_highlight_item:
2392 * Only handles a single track or waypoint ATM
2393 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2395 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2397 // Check the layer for visibility (including all the parents visibilities)
2398 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2401 static struct DrawingParams dp;
2402 init_drawing_params ( &dp, vtl, vvp, TRUE );
2405 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2407 trw_layer_draw_track_cb ( NULL, trk, &dp );
2409 if ( vtl->waypoints_visible && wpt ) {
2410 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2415 * vik_trw_layer_draw_highlight_item:
2417 * Generally for drawing all tracks or routes or waypoints
2418 * trks may be actually routes
2419 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2421 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2423 // Check the layer for visibility (including all the parents visibilities)
2424 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2427 static struct DrawingParams dp;
2428 init_drawing_params ( &dp, vtl, vvp, TRUE );
2431 gboolean is_routes = (trks == vtl->routes);
2432 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2434 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2437 if ( vtl->waypoints_visible && wpts )
2438 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2441 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2444 if ( vtl->track_bg_gc )
2446 g_object_unref ( vtl->track_bg_gc );
2447 vtl->track_bg_gc = NULL;
2449 if ( vtl->track_1color_gc )
2451 g_object_unref ( vtl->track_1color_gc );
2452 vtl->track_1color_gc = NULL;
2454 if ( vtl->current_track_gc )
2456 g_object_unref ( vtl->current_track_gc );
2457 vtl->current_track_gc = NULL;
2459 if ( vtl->current_track_newpoint_gc )
2461 g_object_unref ( vtl->current_track_newpoint_gc );
2462 vtl->current_track_newpoint_gc = NULL;
2465 if ( ! vtl->track_gc )
2467 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2468 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2469 g_array_free ( vtl->track_gc, TRUE );
2470 vtl->track_gc = NULL;
2473 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2475 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2476 gint width = vtl->line_thickness;
2478 if ( vtl->track_gc )
2479 trw_layer_free_track_gcs ( vtl );
2481 if ( vtl->track_bg_gc )
2482 g_object_unref ( vtl->track_bg_gc );
2483 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2485 // Ensure new track drawing heeds line thickness setting
2486 // however always have a minium of 2, as 1 pixel is really narrow
2487 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2489 if ( vtl->current_track_gc )
2490 g_object_unref ( vtl->current_track_gc );
2491 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2492 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2494 // 'newpoint' gc is exactly the same as the current track gc
2495 if ( vtl->current_track_newpoint_gc )
2496 g_object_unref ( vtl->current_track_newpoint_gc );
2497 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2498 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2500 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2502 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2503 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2505 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2506 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2507 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2509 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2511 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2514 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2516 VikTrwLayer *rv = trw_layer_new1 ( vp );
2517 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2519 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2520 /* early exit, as the rest is GUI related */
2524 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2525 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2527 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2528 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2530 trw_layer_new_track_gcs ( rv, vp );
2532 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2533 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2534 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2535 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2537 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2539 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2544 #define SMALL_ICON_SIZE 18
2546 * Can accept a null symbol, and may return null value
2548 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2550 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2551 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2552 // So needing a small icon for the treeview may need some resizing:
2553 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2554 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2558 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2560 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2562 GdkPixbuf *pixbuf = NULL;
2564 if ( track->has_color ) {
2565 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2566 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2567 // Here is some magic found to do the conversion
2568 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2569 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2570 ((track->color.green & 0xff00) << 8) |
2571 (track->color.blue & 0xff00);
2573 gdk_pixbuf_fill ( pixbuf, pixel );
2576 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 );
2579 g_object_unref (pixbuf);
2581 *new_iter = *((GtkTreeIter *) pass_along[1]);
2582 if ( track->is_route )
2583 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2585 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2587 if ( ! track->visible )
2588 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2591 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2593 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2595 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 );
2597 *new_iter = *((GtkTreeIter *) pass_along[1]);
2598 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2600 if ( ! wp->visible )
2601 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2604 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2606 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2609 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2611 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2614 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2616 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2619 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2622 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2624 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2625 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2627 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2629 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2632 if ( g_hash_table_size (vtl->routes) > 0 ) {
2633 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2635 pass_along[0] = &(vtl->routes_iter);
2636 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2638 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2640 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2643 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2644 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2646 pass_along[0] = &(vtl->waypoints_iter);
2647 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2649 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2651 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2656 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2660 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2661 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2662 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2663 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2665 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2667 return (t->visible ^= 1);
2671 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2673 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2675 return (t->visible ^= 1);
2679 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2681 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2683 return (t->visible ^= 1);
2692 * Return a property about tracks for this layer
2694 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2696 return vtl->line_thickness;
2699 // Structure to hold multiple track information for a layer
2708 * Build up layer multiple track information via updating the tooltip_tracks structure
2710 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2712 tt->length = tt->length + vik_track_get_length (tr);
2714 // Ensure times are available
2715 if ( tr->trackpoints &&
2716 vik_track_get_tp_first(tr)->has_timestamp &&
2717 vik_track_get_tp_last(tr)->has_timestamp ) {
2720 t1 = vik_track_get_tp_first(tr)->timestamp;
2721 t2 = vik_track_get_tp_last(tr)->timestamp;
2723 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2724 // Hence initialize to the first 'proper' value
2725 if ( tt->start_time == 0 )
2726 tt->start_time = t1;
2727 if ( tt->end_time == 0 )
2730 // Update find the earliest / last times
2731 if ( t1 < tt->start_time )
2732 tt->start_time = t1;
2733 if ( t2 > tt->end_time )
2736 // Keep track of total time
2737 // there maybe gaps within a track (eg segments)
2738 // but this should be generally good enough for a simple indicator
2739 tt->duration = tt->duration + (int)(t2-t1);
2744 * Generate tooltip text for the layer.
2745 * This is relatively complicated as it considers information for
2746 * no tracks, a single track or multiple tracks
2747 * (which may or may not have timing information)
2749 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2760 static gchar tmp_buf[128];
2763 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2765 // Safety check - I think these should always be valid
2766 if ( vtl->tracks && vtl->waypoints ) {
2767 tooltip_tracks tt = { 0.0, 0, 0 };
2768 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2770 GDate* gdate_start = g_date_new ();
2771 g_date_set_time_t (gdate_start, tt.start_time);
2773 GDate* gdate_end = g_date_new ();
2774 g_date_set_time_t (gdate_end, tt.end_time);
2776 if ( g_date_compare (gdate_start, gdate_end) ) {
2777 // Dates differ so print range on separate line
2778 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2779 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2780 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2783 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2784 if ( tt.start_time != 0 )
2785 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2789 if ( tt.length > 0.0 ) {
2790 gdouble len_in_units;
2792 // Setup info dependent on distance units
2793 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2794 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2795 len_in_units = VIK_METERS_TO_MILES(tt.length);
2798 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2799 len_in_units = tt.length/1000.0;
2802 // Timing information if available
2804 if ( tt.duration > 0 ) {
2805 g_snprintf (tbuf1, sizeof(tbuf1),
2806 _(" in %d:%02d hrs:mins"),
2807 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2809 g_snprintf (tbuf2, sizeof(tbuf2),
2810 _("\n%sTotal Length %.1f %s%s"),
2811 tbuf3, len_in_units, tbuf4, tbuf1);
2814 // Put together all the elements to form compact tooltip text
2815 g_snprintf (tmp_buf, sizeof(tmp_buf),
2816 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2817 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2819 g_date_free (gdate_start);
2820 g_date_free (gdate_end);
2827 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2831 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2833 // Very simple tooltip - may expand detail in the future...
2834 static gchar tmp_buf[32];
2835 g_snprintf (tmp_buf, sizeof(tmp_buf),
2837 g_hash_table_size (l->tracks));
2841 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2843 // Very simple tooltip - may expand detail in the future...
2844 static gchar tmp_buf[32];
2845 g_snprintf (tmp_buf, sizeof(tmp_buf),
2847 g_hash_table_size (l->routes));
2852 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2853 // Same tooltip for a route
2854 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2857 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2858 tr = g_hash_table_lookup ( l->tracks, sublayer );
2860 tr = g_hash_table_lookup ( l->routes, sublayer );
2863 // Could be a better way of handling strings - but this works...
2864 gchar time_buf1[20];
2865 gchar time_buf2[20];
2866 time_buf1[0] = '\0';
2867 time_buf2[0] = '\0';
2868 static gchar tmp_buf[100];
2869 // Compact info: Short date eg (11/20/99), duration and length
2870 // Hopefully these are the things that are most useful and so promoted into the tooltip
2871 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2872 // %x The preferred date representation for the current locale without the time.
2873 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2874 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2875 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2877 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2880 // Get length and consider the appropriate distance units
2881 gdouble tr_len = vik_track_get_length(tr);
2882 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2883 switch (dist_units) {
2884 case VIK_UNITS_DISTANCE_KILOMETRES:
2885 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2887 case VIK_UNITS_DISTANCE_MILES:
2888 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2897 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2899 // Very simple tooltip - may expand detail in the future...
2900 static gchar tmp_buf[32];
2901 g_snprintf (tmp_buf, sizeof(tmp_buf),
2903 g_hash_table_size (l->waypoints));
2907 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2909 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2910 // NB It's OK to return NULL
2915 return w->description;
2924 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2927 * set_statusbar_msg_info_trkpt:
2929 * Function to show track point information on the statusbar
2930 * Items displayed is controlled by the settings format code
2932 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2934 gchar *statusbar_format_code = NULL;
2935 gboolean need2free = FALSE;
2936 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2937 // Otherwise use default
2938 statusbar_format_code = g_strdup ( "KEATDN" );
2942 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2943 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2947 g_free ( statusbar_format_code );
2951 * Function to show basic waypoint information on the statusbar
2953 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2956 switch (a_vik_get_units_height ()) {
2957 case VIK_UNITS_HEIGHT_FEET:
2958 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2961 //VIK_UNITS_HEIGHT_METRES:
2962 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2966 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2967 // one can easily use the current pointer position to see this if needed
2968 gchar *lat = NULL, *lon = NULL;
2969 static struct LatLon ll;
2970 vik_coord_to_latlon (&(wpt->coord), &ll);
2971 a_coords_latlon_to_string ( &ll, &lat, &lon );
2973 // Combine parts to make overall message
2976 // Add comment if available
2977 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2979 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2980 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2987 * General layer selection function, find out which bit is selected and take appropriate action
2989 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2992 l->current_wp = NULL;
2993 l->current_wp_id = NULL;
2994 trw_layer_cancel_current_tp ( l, FALSE );
2997 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3001 case VIK_TREEVIEW_TYPE_LAYER:
3003 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3004 /* Mark for redraw */
3009 case VIK_TREEVIEW_TYPE_SUBLAYER:
3013 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3015 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3016 /* Mark for redraw */
3020 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3022 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3023 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3024 /* Mark for redraw */
3028 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3030 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3031 /* Mark for redraw */
3035 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3037 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3038 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3039 /* Mark for redraw */
3043 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3045 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3046 /* Mark for redraw */
3050 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3052 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3054 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3055 // Show some waypoint info
3056 set_statusbar_msg_info_wpt ( l, wpt );
3057 /* Mark for redraw */
3064 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3073 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3078 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3083 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3088 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3090 return l->waypoints;
3093 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3095 return vtl->tracks_iters;
3098 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3100 return vtl->routes_iters;
3103 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3105 return vtl->waypoints;
3108 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3110 return ! ( g_hash_table_size ( vtl->tracks ) ||
3111 g_hash_table_size ( vtl->routes ) ||
3112 g_hash_table_size ( vtl->waypoints ) );
3115 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3117 return vtl->tracks_visible;
3120 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3122 return vtl->routes_visible;
3125 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3127 return vtl->waypoints_visible;
3131 * ATM use a case sensitive find
3132 * Finds the first one
3134 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3136 if ( wp && wp->name )
3137 if ( ! strcmp ( wp->name, name ) )
3143 * Get waypoint by name - not guaranteed to be unique
3144 * Finds the first one
3146 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3148 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3152 * ATM use a case sensitive find
3153 * Finds the first one
3155 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3157 if ( trk && trk->name )
3158 if ( ! strcmp ( trk->name, name ) )
3164 * Get track by name - not guaranteed to be unique
3165 * Finds the first one
3167 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3169 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3173 * Get route by name - not guaranteed to be unique
3174 * Finds the first one
3176 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3178 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3181 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3183 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3184 maxmin[0].lat = trk->bbox.north;
3185 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3186 maxmin[1].lat = trk->bbox.south;
3187 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3188 maxmin[0].lon = trk->bbox.east;
3189 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3190 maxmin[1].lon = trk->bbox.west;
3193 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3195 // Continually reuse maxmin to find the latest maximum and minimum values
3196 // First set to waypoints bounds
3197 maxmin[0].lat = vtl->waypoints_bbox.north;
3198 maxmin[1].lat = vtl->waypoints_bbox.south;
3199 maxmin[0].lon = vtl->waypoints_bbox.east;
3200 maxmin[1].lon = vtl->waypoints_bbox.west;
3201 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3202 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3205 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3207 /* 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... */
3208 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3209 trw_layer_find_maxmin (vtl, maxmin);
3210 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3214 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3215 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3220 static void trw_layer_centerize ( menu_array_layer values )
3222 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3224 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3225 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3227 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3230 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3232 /* First set the center [in case previously viewing from elsewhere] */
3233 /* Then loop through zoom levels until provided positions are in view */
3234 /* This method is not particularly fast - but should work well enough */
3235 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3237 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3238 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3240 /* Convert into definite 'smallest' and 'largest' positions */
3241 struct LatLon minmin;
3242 if ( maxmin[0].lat < maxmin[1].lat )
3243 minmin.lat = maxmin[0].lat;
3245 minmin.lat = maxmin[1].lat;
3247 struct LatLon maxmax;
3248 if ( maxmin[0].lon > maxmin[1].lon )
3249 maxmax.lon = maxmin[0].lon;
3251 maxmax.lon = maxmin[1].lon;
3253 /* Never zoom in too far - generally not that useful, as too close ! */
3254 /* Always recalculate the 'best' zoom level */
3256 vik_viewport_set_zoom ( vvp, zoom );
3258 gdouble min_lat, max_lat, min_lon, max_lon;
3259 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3260 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3261 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3262 /* NB I think the logic used in this test to determine if the bounds is within view
3263 fails if track goes across 180 degrees longitude.
3264 Hopefully that situation is not too common...
3265 Mind you viking doesn't really do edge locations to well anyway */
3266 if ( min_lat < minmin.lat &&
3267 max_lat > minmin.lat &&
3268 min_lon < maxmax.lon &&
3269 max_lon > maxmax.lon )
3270 /* Found within zoom level */
3275 vik_viewport_set_zoom ( vvp, zoom );
3279 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3281 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3282 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3283 trw_layer_find_maxmin (vtl, maxmin);
3284 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3287 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3292 static void trw_layer_auto_view ( menu_array_layer values )
3294 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3295 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3296 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3297 vik_layers_panel_emit_update ( vlp );
3300 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3303 static void trw_layer_export_gpspoint ( menu_array_layer values )
3305 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3307 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3309 g_free ( auto_save_name );
3312 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3314 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3316 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3318 g_free ( auto_save_name );
3321 static void trw_layer_export_gpx ( menu_array_layer values )
3323 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3325 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3327 g_free ( auto_save_name );
3330 static void trw_layer_export_kml ( menu_array_layer values )
3332 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3334 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3336 g_free ( auto_save_name );
3339 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3341 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3342 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3345 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3347 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3350 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3352 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3355 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3357 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3359 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3360 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3362 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3364 if ( !trk || !trk->name )
3367 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3369 gchar *label = NULL;
3370 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3371 label = _("Export Route as GPX");
3373 label = _("Export Track as GPX");
3374 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3376 g_free ( auto_save_name );
3379 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3381 wpu_udata *user_data = udata;
3382 if ( wp == user_data->wp ) {
3383 user_data->uuid = id;
3389 static void trw_layer_goto_wp ( menu_array_layer values )
3391 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3392 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3393 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3394 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3395 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3397 GTK_RESPONSE_REJECT,
3399 GTK_RESPONSE_ACCEPT,
3402 GtkWidget *label, *entry;
3403 label = gtk_label_new(_("Waypoint Name:"));
3404 entry = gtk_entry_new();
3406 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3407 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3408 gtk_widget_show_all ( label );
3409 gtk_widget_show_all ( entry );
3411 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3413 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3415 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3416 // Find *first* wp with the given name
3417 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3420 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3423 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3424 vik_layers_panel_emit_update ( vlp );
3426 // Find and select on the side panel
3431 // Hmmm, want key of it
3432 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3434 if ( wpf && udata.uuid ) {
3435 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3436 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3445 gtk_widget_destroy ( dia );
3448 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3450 gchar *default_name = highest_wp_number_get(vtl);
3451 VikWaypoint *wp = vik_waypoint_new();
3452 gchar *returned_name;
3454 wp->coord = *def_coord;
3456 // Attempt to auto set height if DEM data is available
3457 vik_waypoint_apply_dem_data ( wp, TRUE );
3459 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3461 if ( returned_name )
3464 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3465 g_free (default_name);
3466 g_free (returned_name);
3469 g_free (default_name);
3470 vik_waypoint_free(wp);
3474 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3476 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3477 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3478 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3479 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3480 VikViewport *vvp = vik_window_viewport(vw);
3482 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3483 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3484 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3485 trw_layer_calculate_bounds_waypoints ( vtl );
3486 vik_layers_panel_emit_update ( vlp );
3489 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3491 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3492 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3493 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3495 trw_layer_find_maxmin (vtl, maxmin);
3496 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3497 trw_layer_calculate_bounds_waypoints ( vtl );
3498 vik_layers_panel_emit_update ( vlp );
3501 #ifdef VIK_CONFIG_GEOTAG
3502 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3504 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3506 // Update directly - not changing the mtime
3507 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3510 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3512 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3515 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3519 * Use code in separate file for this feature as reasonably complex
3521 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3523 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3524 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3525 // Unset so can be reverified later if necessary
3526 vtl->has_verified_thumbnails = FALSE;
3528 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3534 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3536 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3537 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3539 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3545 static void trw_layer_geotagging ( menu_array_layer values )
3547 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3548 // Unset so can be reverified later if necessary
3549 vtl->has_verified_thumbnails = FALSE;
3551 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3558 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3560 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3562 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3563 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3564 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3565 VikViewport *vvp = vik_window_viewport(vw);
3567 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3571 * Acquire into this TRW Layer straight from GPS Device
3573 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3575 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3576 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3580 * Acquire into this TRW Layer from Directions
3582 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3584 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3588 * Acquire into this TRW Layer from an entered URL
3590 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3592 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3593 trw_layer_acquire ( values, &vik_datasource_url_interface );
3596 #ifdef VIK_CONFIG_OPENSTREETMAP
3598 * Acquire into this TRW Layer from OSM
3600 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3602 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3606 * Acquire into this TRW Layer from OSM for 'My' Traces
3608 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3610 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3614 #ifdef VIK_CONFIG_GEOCACHES
3616 * Acquire into this TRW Layer from Geocaching.com
3618 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3620 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3624 #ifdef VIK_CONFIG_GEOTAG
3626 * Acquire into this TRW Layer from images
3628 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3630 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3632 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3633 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3635 // Reverify thumbnails as they may have changed
3636 vtl->has_verified_thumbnails = FALSE;
3637 trw_layer_verify_thumbnails ( vtl, NULL );
3641 static void trw_layer_gps_upload ( menu_array_layer values )
3643 menu_array_sublayer data;
3645 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3647 data[MA_VTL] = values[MA_VTL];
3648 data[MA_VLP] = values[MA_VLP];
3650 trw_layer_gps_upload_any ( data );
3654 * If pass_along[3] is defined that this will upload just that track
3656 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3658 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3659 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3661 // May not actually get a track here as values[2&3] can be null
3662 VikTrack *track = NULL;
3663 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3664 gboolean xfer_all = FALSE;
3666 if ( values[MA_SUBTYPE] ) {
3668 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3669 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3672 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3673 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3676 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3679 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3683 else if ( !values[MA_CONFIRM] )
3684 xfer_all = TRUE; // i.e. whole layer
3686 if (track && !track->visible) {
3687 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3691 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3692 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3693 GTK_DIALOG_DESTROY_WITH_PARENT,
3695 GTK_RESPONSE_ACCEPT,
3697 GTK_RESPONSE_REJECT,
3700 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3701 GtkWidget *response_w = NULL;
3702 #if GTK_CHECK_VERSION (2, 20, 0)
3703 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3707 gtk_widget_grab_focus ( response_w );
3709 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3711 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3712 datasource_gps_clean_up ( dgs );
3713 gtk_widget_destroy ( dialog );
3717 // Get info from reused datasource dialog widgets
3718 gchar* protocol = datasource_gps_get_protocol ( dgs );
3719 gchar* port = datasource_gps_get_descriptor ( dgs );
3720 // NB don't free the above strings as they're references to values held elsewhere
3721 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3722 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3723 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3724 gboolean turn_off = datasource_gps_get_off ( dgs );
3726 gtk_widget_destroy ( dialog );
3728 // When called from the viewport - work the corresponding layerspanel:
3730 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3733 // Apply settings to transfer to the GPS device
3740 vik_layers_panel_get_viewport (vlp),
3749 * Acquire into this TRW Layer from any GPS Babel supported file
3751 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3753 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3754 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3755 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3756 VikViewport *vvp = vik_window_viewport(vw);
3758 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3761 static void trw_layer_new_wp ( menu_array_layer values )
3763 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3764 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3765 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3766 instead return true if you want to update. */
3767 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 ) {
3768 trw_layer_calculate_bounds_waypoints ( vtl );
3769 vik_layers_panel_emit_update ( vlp );
3773 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3775 vtl->current_track = vik_track_new();
3776 vik_track_set_defaults ( vtl->current_track );
3777 vtl->current_track->visible = TRUE;
3778 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3779 // Create track with the preferred colour from the layer properties
3780 vtl->current_track->color = vtl->track_color;
3782 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3783 vtl->current_track->has_color = TRUE;
3784 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3787 static void trw_layer_new_track ( menu_array_layer values )
3789 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3791 if ( ! vtl->current_track ) {
3792 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3793 new_track_create_common ( vtl, name );
3796 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3800 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3802 vtl->current_track = vik_track_new();
3803 vik_track_set_defaults ( vtl->current_track );
3804 vtl->current_track->visible = TRUE;
3805 vtl->current_track->is_route = TRUE;
3806 // By default make all routes red
3807 vtl->current_track->has_color = TRUE;
3808 gdk_color_parse ( "red", &vtl->current_track->color );
3809 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3812 static void trw_layer_new_route ( menu_array_layer values )
3814 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3816 if ( ! vtl->current_track ) {
3817 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3818 new_route_create_common ( vtl, name );
3820 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3824 static void trw_layer_auto_routes_view ( menu_array_layer values )
3826 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3827 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3829 if ( g_hash_table_size (vtl->routes) > 0 ) {
3830 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3831 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3832 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3833 vik_layers_panel_emit_update ( vlp );
3838 static void trw_layer_finish_track ( menu_array_layer values )
3840 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3841 vtl->current_track = NULL;
3842 vik_layer_emit_update ( VIK_LAYER(vtl) );
3845 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3847 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3848 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3850 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3851 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3852 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3853 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3854 vik_layers_panel_emit_update ( vlp );
3858 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3860 /* NB do not care if wp is visible or not */
3861 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3864 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3866 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3867 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3869 /* Only 1 waypoint - jump straight to it */
3870 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3871 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3872 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3874 /* If at least 2 waypoints - find center and then zoom to fit */
3875 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3877 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3878 maxmin[0].lat = vtl->waypoints_bbox.north;
3879 maxmin[1].lat = vtl->waypoints_bbox.south;
3880 maxmin[0].lon = vtl->waypoints_bbox.east;
3881 maxmin[1].lon = vtl->waypoints_bbox.west;
3882 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3885 vik_layers_panel_emit_update ( vlp );
3888 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3890 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3893 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3895 if ( values[MA_MISC] ) {
3896 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3897 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3901 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3903 static menu_array_layer pass_along;
3905 GtkWidget *export_submenu;
3906 pass_along[MA_VTL] = vtl;
3907 pass_along[MA_VLP] = vlp;
3909 item = gtk_menu_item_new();
3910 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3911 gtk_widget_show ( item );
3913 if ( vtl->current_track ) {
3914 if ( vtl->current_track->is_route )
3915 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3917 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3919 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3920 gtk_widget_show ( item );
3923 item = gtk_menu_item_new ();
3924 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3925 gtk_widget_show ( item );
3928 /* Now with icons */
3929 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3930 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3931 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3932 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3933 gtk_widget_show ( item );
3935 GtkWidget *view_submenu = gtk_menu_new();
3936 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3937 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3938 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3939 gtk_widget_show ( item );
3940 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3942 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3944 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3945 gtk_widget_show ( item );
3947 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3949 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3950 gtk_widget_show ( item );
3952 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3954 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3955 gtk_widget_show ( item );
3957 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3958 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3961 gtk_widget_show ( item );
3963 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3964 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3965 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3966 gtk_widget_show ( item );
3968 export_submenu = gtk_menu_new ();
3969 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3970 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3971 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3972 gtk_widget_show ( item );
3973 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3975 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3977 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3978 gtk_widget_show ( item );
3980 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3981 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3982 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3983 gtk_widget_show ( item );
3985 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3988 gtk_widget_show ( item );
3990 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3992 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3993 gtk_widget_show ( item );
3995 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
3996 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
3997 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3998 gtk_widget_show ( item );
4000 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4001 item = gtk_menu_item_new_with_mnemonic ( external1 );
4002 g_free ( external1 );
4003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4004 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4005 gtk_widget_show ( item );
4007 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4008 item = gtk_menu_item_new_with_mnemonic ( external2 );
4009 g_free ( external2 );
4010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4011 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4012 gtk_widget_show ( item );
4014 GtkWidget *new_submenu = gtk_menu_new();
4015 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4016 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4017 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4018 gtk_widget_show(item);
4019 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4021 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4022 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4024 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4025 gtk_widget_show ( item );
4027 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4028 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4030 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4031 gtk_widget_show ( item );
4032 // Make it available only when a new track *not* already in progress
4033 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4035 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4036 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4038 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4039 gtk_widget_show ( item );
4040 // Make it available only when a new track *not* already in progress
4041 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4043 #ifdef VIK_CONFIG_GEOTAG
4044 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4046 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4047 gtk_widget_show ( item );
4050 GtkWidget *acquire_submenu = gtk_menu_new ();
4051 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4053 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4054 gtk_widget_show ( item );
4055 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4057 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4059 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4060 gtk_widget_show ( item );
4062 /* FIXME: only add menu when at least a routing engine has support for Directions */
4063 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4065 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4066 gtk_widget_show ( item );
4068 #ifdef VIK_CONFIG_OPENSTREETMAP
4069 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4071 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4072 gtk_widget_show ( item );
4074 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4076 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4077 gtk_widget_show ( item );
4080 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4082 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4083 gtk_widget_show ( item );
4085 #ifdef VIK_CONFIG_GEONAMES
4086 GtkWidget *wikipedia_submenu = gtk_menu_new();
4087 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4089 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4090 gtk_widget_show(item);
4091 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4093 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4096 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4097 gtk_widget_show ( item );
4099 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4102 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4103 gtk_widget_show ( item );
4106 #ifdef VIK_CONFIG_GEOCACHES
4107 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4109 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4110 gtk_widget_show ( item );
4113 #ifdef VIK_CONFIG_GEOTAG
4114 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4117 gtk_widget_show ( item );
4120 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4122 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4123 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4124 gtk_widget_show ( item );
4126 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4128 GtkWidget *upload_submenu = gtk_menu_new ();
4129 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4131 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4132 gtk_widget_show ( item );
4133 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4135 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4136 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4137 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4138 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4139 gtk_widget_show ( item );
4141 #ifdef VIK_CONFIG_OPENSTREETMAP
4142 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4145 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4146 gtk_widget_show ( item );
4149 GtkWidget *delete_submenu = gtk_menu_new ();
4150 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4151 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4152 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4153 gtk_widget_show ( item );
4154 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4156 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4159 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4160 gtk_widget_show ( item );
4162 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4165 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4166 gtk_widget_show ( item );
4168 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4171 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4172 gtk_widget_show ( item );
4174 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4177 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4178 gtk_widget_show ( item );
4180 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4183 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4184 gtk_widget_show ( item );
4186 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4189 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4190 gtk_widget_show ( item );
4192 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4193 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4195 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4196 gtk_widget_show ( item );
4199 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4200 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4202 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4203 gtk_widget_show ( item );
4206 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4209 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4210 gtk_widget_show ( item );
4211 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4216 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4217 gtk_widget_show ( item );
4218 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4221 // Fake Waypoint UUIDs vi simple increasing integer
4222 static guint wp_uuid = 0;
4224 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4228 vik_waypoint_set_name (wp, name);
4230 if ( VIK_LAYER(vtl)->realized )
4232 // Do we need to create the sublayer:
4233 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4234 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4237 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4239 // Visibility column always needed for waypoints
4240 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 );
4242 // Actual setting of visibility dependent on the waypoint
4243 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4245 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4247 // Sort now as post_read is not called on a realized waypoint
4248 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4251 highest_wp_number_add_wp(vtl, name);
4252 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4256 // Fake Track UUIDs vi simple increasing integer
4257 static guint tr_uuid = 0;
4259 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4263 vik_track_set_name (t, name);
4265 if ( VIK_LAYER(vtl)->realized )
4267 // Do we need to create the sublayer:
4268 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4269 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4272 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4273 // Visibility column always needed for tracks
4274 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 );
4276 // Actual setting of visibility dependent on the track
4277 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4279 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4281 // Sort now as post_read is not called on a realized track
4282 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4285 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4287 trw_layer_update_treeview ( vtl, t );
4290 // Fake Route UUIDs vi simple increasing integer
4291 static guint rt_uuid = 0;
4293 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4297 vik_track_set_name (t, name);
4299 if ( VIK_LAYER(vtl)->realized )
4301 // Do we need to create the sublayer:
4302 if ( g_hash_table_size (vtl->routes) == 0 ) {
4303 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4306 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4307 // Visibility column always needed for routes
4308 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 );
4309 // Actual setting of visibility dependent on the route
4310 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4312 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4314 // Sort now as post_read is not called on a realized route
4315 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4318 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4320 trw_layer_update_treeview ( vtl, t );
4323 /* to be called whenever a track has been deleted or may have been changed. */
4324 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4326 if (vtl->current_tp_track == trk )
4327 trw_layer_cancel_current_tp ( vtl, FALSE );
4331 * Normally this is done to due the waypoint size preference having changed
4333 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4335 GHashTableIter iter;
4336 gpointer key, value;
4339 g_hash_table_iter_init ( &iter, vtl->waypoints );
4340 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4341 VikWaypoint *wp = VIK_WAYPOINT(value);
4343 // Reapply symbol setting to update the pixbuf
4344 gchar *tmp_symbol = g_strdup ( wp->symbol );
4345 vik_waypoint_set_symbol ( wp, tmp_symbol );
4346 g_free ( tmp_symbol );
4352 * trw_layer_new_unique_sublayer_name:
4354 * Allocates a unique new name
4356 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4359 gchar *newname = g_strdup(name);
4364 switch ( sublayer_type ) {
4365 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4366 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4368 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4369 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4372 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4375 // If found a name already in use try adding 1 to it and we try again
4377 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4379 newname = new_newname;
4382 } while ( id != NULL);
4387 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4389 // No more uniqueness of name forced when loading from a file
4390 // This now makes this function a little redunant as we just flow the parameters through
4391 vik_trw_layer_add_waypoint ( vtl, name, wp );
4394 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4396 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4397 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4398 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4399 vik_track_free ( tr );
4400 vtl->route_finder_append = FALSE; /* this means we have added it */
4403 // No more uniqueness of name forced when loading from a file
4405 vik_trw_layer_add_route ( vtl, name, tr );
4407 vik_trw_layer_add_track ( vtl, name, tr );
4409 if ( vtl->route_finder_check_added_track ) {
4410 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4411 vtl->route_finder_added_track = tr;
4416 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4418 *l = g_list_append(*l, id);
4422 * Move an item from one TRW layer to another TRW layer
4424 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4426 // TODO reconsider strategy when moving within layer (if anything...)
4427 gboolean rename = ( vtl_src != vtl_dest );
4431 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4432 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4436 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4438 newname = g_strdup ( trk->name );
4440 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4441 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4443 vik_trw_layer_delete_track ( vtl_src, trk );
4446 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4447 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4451 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4453 newname = g_strdup ( trk->name );
4455 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4456 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4458 vik_trw_layer_delete_route ( vtl_src, trk );
4461 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4462 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4466 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4468 newname = g_strdup ( wp->name );
4470 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4471 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4473 trw_layer_delete_waypoint ( vtl_src, wp );
4475 // Recalculate bounds even if not renamed as maybe dragged between layers
4476 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4477 trw_layer_calculate_bounds_waypoints ( vtl_src );
4481 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4483 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4484 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4486 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4487 GList *items = NULL;
4490 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4491 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4493 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4494 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4496 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4497 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4502 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4503 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4504 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4505 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4507 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4514 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4515 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4519 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4521 trku_udata *user_data = udata;
4522 if ( trk == user_data->trk ) {
4523 user_data->uuid = id;
4529 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4531 gboolean was_visible = FALSE;
4532 if ( trk && trk->name ) {
4534 if ( trk == vtl->current_track ) {
4535 vtl->current_track = NULL;
4536 vtl->current_tp_track = NULL;
4537 vtl->current_tp_id = NULL;
4538 vtl->moving_tp = FALSE;
4541 was_visible = trk->visible;
4543 if ( trk == vtl->route_finder_current_track )
4544 vtl->route_finder_current_track = NULL;
4546 if ( trk == vtl->route_finder_added_track )
4547 vtl->route_finder_added_track = NULL;
4553 // Hmmm, want key of it
4554 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4556 if ( trkf && udata.uuid ) {
4557 /* could be current_tp, so we have to check */
4558 trw_layer_cancel_tps_of_track ( vtl, trk );
4560 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4563 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4564 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4565 g_hash_table_remove ( vtl->tracks, udata.uuid );
4567 // If last sublayer, then remove sublayer container
4568 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4569 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4572 // Incase it was selected (no item delete signal ATM)
4573 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4579 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4581 gboolean was_visible = FALSE;
4583 if ( trk && trk->name ) {
4585 if ( trk == vtl->current_track ) {
4586 vtl->current_track = NULL;
4587 vtl->current_tp_track = NULL;
4588 vtl->current_tp_id = NULL;
4589 vtl->moving_tp = FALSE;
4592 was_visible = trk->visible;
4594 if ( trk == vtl->route_finder_current_track )
4595 vtl->route_finder_current_track = NULL;
4597 if ( trk == vtl->route_finder_added_track )
4598 vtl->route_finder_added_track = NULL;
4604 // Hmmm, want key of it
4605 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4607 if ( trkf && udata.uuid ) {
4608 /* could be current_tp, so we have to check */
4609 trw_layer_cancel_tps_of_track ( vtl, trk );
4611 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4614 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4615 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4616 g_hash_table_remove ( vtl->routes, udata.uuid );
4618 // If last sublayer, then remove sublayer container
4619 if ( g_hash_table_size (vtl->routes) == 0 ) {
4620 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4623 // Incase it was selected (no item delete signal ATM)
4624 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4630 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4632 gboolean was_visible = FALSE;
4634 if ( wp && wp->name ) {
4636 if ( wp == vtl->current_wp ) {
4637 vtl->current_wp = NULL;
4638 vtl->current_wp_id = NULL;
4639 vtl->moving_wp = FALSE;
4642 was_visible = wp->visible;
4648 // Hmmm, want key of it
4649 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4651 if ( wpf && udata.uuid ) {
4652 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4655 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4656 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4658 highest_wp_number_remove_wp(vtl, wp->name);
4659 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4661 // If last sublayer, then remove sublayer container
4662 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4663 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4666 // Incase it was selected (no item delete signal ATM)
4667 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4675 // Only for temporary use by trw_layer_delete_waypoint_by_name
4676 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4678 wpu_udata *user_data = udata;
4679 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4680 user_data->uuid = id;
4687 * Delete a waypoint by the given name
4688 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4689 * as there be multiple waypoints with the same name
4691 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4694 // Fake a waypoint with the given name
4695 udata.wp = vik_waypoint_new ();
4696 vik_waypoint_set_name (udata.wp, name);
4697 // Currently only the name is used in this waypoint find function
4700 // Hmmm, want key of it
4701 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4703 vik_waypoint_free (udata.wp);
4705 if ( wpf && udata.uuid )
4706 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4712 VikTrack *trk; // input
4713 gpointer uuid; // output
4716 // Only for temporary use by trw_layer_delete_track_by_name
4717 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4719 tpu_udata *user_data = udata;
4720 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4721 user_data->uuid = id;
4728 * Delete a track by the given name
4729 * NOTE: ATM this will delete the first encountered Track with the specified name
4730 * as there may be multiple tracks with the same name within the specified hash table
4732 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4735 // Fake a track with the given name
4736 udata.trk = vik_track_new ();
4737 vik_track_set_name (udata.trk, name);
4738 // Currently only the name is used in this waypoint find function
4741 // Hmmm, want key of it
4742 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4744 vik_track_free (udata.trk);
4746 if ( trkf && udata.uuid ) {
4747 // This could be a little better written...
4748 if ( vtl->tracks == ht_tracks )
4749 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4750 if ( vtl->routes == ht_tracks )
4751 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4758 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4760 vik_treeview_item_delete (vt, it );
4763 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4766 vtl->current_track = NULL;
4767 vtl->route_finder_current_track = NULL;
4768 vtl->route_finder_added_track = NULL;
4769 if (vtl->current_tp_track)
4770 trw_layer_cancel_current_tp(vtl, FALSE);
4772 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4773 g_hash_table_remove_all(vtl->routes_iters);
4774 g_hash_table_remove_all(vtl->routes);
4776 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4778 vik_layer_emit_update ( VIK_LAYER(vtl) );
4781 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4784 vtl->current_track = NULL;
4785 vtl->route_finder_current_track = NULL;
4786 vtl->route_finder_added_track = NULL;
4787 if (vtl->current_tp_track)
4788 trw_layer_cancel_current_tp(vtl, FALSE);
4790 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4791 g_hash_table_remove_all(vtl->tracks_iters);
4792 g_hash_table_remove_all(vtl->tracks);
4794 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4796 vik_layer_emit_update ( VIK_LAYER(vtl) );
4799 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4801 vtl->current_wp = NULL;
4802 vtl->current_wp_id = NULL;
4803 vtl->moving_wp = FALSE;
4805 highest_wp_number_reset(vtl);
4807 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4808 g_hash_table_remove_all(vtl->waypoints_iters);
4809 g_hash_table_remove_all(vtl->waypoints);
4811 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4813 vik_layer_emit_update ( VIK_LAYER(vtl) );
4816 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4818 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4819 // Get confirmation from the user
4820 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4821 _("Are you sure you want to delete all tracks in %s?"),
4822 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4823 vik_trw_layer_delete_all_tracks (vtl);
4826 static void trw_layer_delete_all_routes ( menu_array_layer values )
4828 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4829 // Get confirmation from the user
4830 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4831 _("Are you sure you want to delete all routes in %s?"),
4832 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4833 vik_trw_layer_delete_all_routes (vtl);
4836 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4838 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4839 // Get confirmation from the user
4840 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4841 _("Are you sure you want to delete all waypoints in %s?"),
4842 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4843 vik_trw_layer_delete_all_waypoints (vtl);
4846 static void trw_layer_delete_item ( menu_array_sublayer values )
4848 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4849 gboolean was_visible = FALSE;
4850 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4852 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4853 if ( wp && wp->name ) {
4854 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4855 // Get confirmation from the user
4856 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4857 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4858 _("Are you sure you want to delete the waypoint \"%s\"?"),
4861 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4864 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4866 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4867 if ( trk && trk->name ) {
4868 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4869 // Get confirmation from the user
4870 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4871 _("Are you sure you want to delete the track \"%s\"?"),
4874 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4879 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4880 if ( trk && trk->name ) {
4881 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4882 // Get confirmation from the user
4883 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4884 _("Are you sure you want to delete the route \"%s\"?"),
4887 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4891 vik_layer_emit_update ( VIK_LAYER(vtl) );
4895 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4897 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4899 vik_waypoint_set_name ( wp, new_name );
4901 // Now update the treeview as well
4906 // Need key of it for treeview update
4907 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4909 if ( wpf && udataU.uuid ) {
4910 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4913 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4914 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4920 * Maintain icon of waypoint in the treeview
4922 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4924 // update the treeview
4929 // Need key of it for treeview update
4930 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4932 if ( wpf && udataU.uuid ) {
4933 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4936 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4941 static void trw_layer_properties_item ( menu_array_sublayer values )
4943 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4944 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4946 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4948 if ( wp && wp->name )
4950 gboolean updated = FALSE;
4951 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4953 trw_layer_waypoint_rename ( vtl, wp, new_name );
4955 if ( updated && values[MA_TV_ITER] )
4956 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4958 if ( updated && VIK_LAYER(vtl)->visible )
4959 vik_layer_emit_update ( VIK_LAYER(vtl) );
4965 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4966 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4968 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4970 if ( tr && tr->name )
4972 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4983 * trw_layer_track_statistics:
4985 * Show track statistics.
4986 * ATM jump to the stats page in the properties
4987 * TODO: consider separating the stats into an individual dialog?
4989 static void trw_layer_track_statistics ( menu_array_sublayer values )
4991 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4993 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4994 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4996 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4998 if ( trk && trk->name ) {
4999 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5009 * Update the treeview of the track id - primarily to update the icon
5011 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5017 gpointer *trkf = NULL;
5018 if ( trk->is_route )
5019 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5021 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5023 if ( trkf && udata.uuid ) {
5025 GtkTreeIter *iter = NULL;
5026 if ( trk->is_route )
5027 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5029 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5032 // TODO: Make this a function
5033 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5034 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5035 ((trk->color.green & 0xff00) << 8) |
5036 (trk->color.blue & 0xff00);
5037 gdk_pixbuf_fill ( pixbuf, pixel );
5038 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5039 g_object_unref (pixbuf);
5046 Parameter 1 -> VikLayersPanel
5047 Parameter 2 -> VikLayer
5048 Parameter 3 -> VikViewport
5050 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5053 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5054 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5057 /* since vlp not set, vl & vvp should be valid instead! */
5059 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5060 vik_layer_emit_update ( VIK_LAYER(vl) );
5065 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5067 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5069 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5070 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5072 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5074 if ( track && track->trackpoints )
5075 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5078 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5080 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5082 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5083 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5085 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5087 if ( track && track->trackpoints )
5089 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5091 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5092 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5093 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5094 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5095 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5099 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5101 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5103 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5104 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5106 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5111 // Converting a track to a route can be a bit more complicated,
5112 // so give a chance to change our minds:
5113 if ( !trk->is_route &&
5114 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5115 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5117 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5118 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5123 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5126 trk_copy->is_route = !trk_copy->is_route;
5128 // ATM can't set name to self - so must create temporary copy
5129 gchar *name = g_strdup ( trk_copy->name );
5131 // Delete old one and then add new one
5132 if ( trk->is_route ) {
5133 vik_trw_layer_delete_route ( vtl, trk );
5134 vik_trw_layer_add_track ( vtl, name, trk_copy );
5137 // Extra route conversion bits...
5138 vik_track_merge_segments ( trk_copy );
5139 vik_track_to_routepoints ( trk_copy );
5141 vik_trw_layer_delete_track ( vtl, trk );
5142 vik_trw_layer_add_route ( vtl, name, trk_copy );
5146 // Update in case color of track / route changes when moving between sublayers
5147 vik_layer_emit_update ( VIK_LAYER(vtl) );
5150 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5152 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5154 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5155 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5157 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5160 vik_track_anonymize_times ( track );
5163 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5165 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5167 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5168 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5170 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5175 vtl->current_track = track;
5176 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);
5178 if ( track->trackpoints )
5179 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5183 * extend a track using route finder
5185 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5187 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5188 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5191 if ( !track->trackpoints )
5194 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5195 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5196 vtl->route_finder_current_track = track;
5197 vtl->route_finder_started = TRUE;
5199 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5205 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5207 // If have a vlp then perform a basic test to see if any DEM info available...
5209 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5211 if ( !g_list_length(dems) ) {
5212 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5220 * apply_dem_data_common:
5222 * A common function for applying the DEM values and reporting the results.
5224 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5226 if ( !trw_layer_dem_test ( vtl, vlp ) )
5229 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5230 // Inform user how much was changed
5232 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5233 g_snprintf(str, 64, tmp_str, changed);
5234 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5237 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5239 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5241 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5242 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5244 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5247 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5250 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5252 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5254 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5255 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5257 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5260 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5266 * A common function for applying the elevation smoothing and reporting the results.
5268 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5270 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5271 // Inform user how much was changed
5273 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5274 g_snprintf(str, 64, tmp_str, changed);
5275 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5281 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5283 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5285 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5286 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5288 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5293 smooth_it ( vtl, track, FALSE );
5296 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5298 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5300 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5301 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5303 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5308 smooth_it ( vtl, track, TRUE );
5312 * Commonal helper function
5314 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5317 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5318 g_snprintf(str, 64, tmp_str, changed);
5319 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5322 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5324 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5325 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5327 if ( !trw_layer_dem_test ( vtl, vlp ) )
5331 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5333 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5335 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5339 GHashTableIter iter;
5340 gpointer key, value;
5342 g_hash_table_iter_init ( &iter, vtl->waypoints );
5343 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5344 VikWaypoint *wp = VIK_WAYPOINT(value);
5345 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5348 wp_changed_message ( vtl, changed );
5351 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5353 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5354 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5356 if ( !trw_layer_dem_test ( vtl, vlp ) )
5360 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5362 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5364 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5368 GHashTableIter iter;
5369 gpointer key, value;
5371 g_hash_table_iter_init ( &iter, vtl->waypoints );
5372 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5373 VikWaypoint *wp = VIK_WAYPOINT(value);
5374 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5377 wp_changed_message ( vtl, changed );
5380 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5382 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5384 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5385 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5387 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5391 if ( !track->trackpoints )
5393 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5396 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5398 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5400 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5401 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5403 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5408 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5411 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5414 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5416 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5418 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5419 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5421 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5426 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5429 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5432 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5434 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5436 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5437 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5439 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5444 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5447 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5451 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5453 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5455 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5457 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5458 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5460 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5462 if ( trk && trk->trackpoints )
5464 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5465 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5466 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5467 if ( values[MA_VLP] )
5468 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5470 vik_layer_emit_update ( VIK_LAYER(vtl) );
5475 * Refine the selected track/route with a routing engine.
5476 * The routing engine is selected by the user, when requestiong the job.
5478 static void trw_layer_route_refine ( menu_array_sublayer values )
5480 static gint last_engine = 0;
5481 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5484 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5485 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5487 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5489 if ( trk && trk->trackpoints )
5491 /* Check size of the route */
5492 int nb = vik_track_get_tp_count(trk);
5494 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5495 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5496 GTK_MESSAGE_WARNING,
5497 GTK_BUTTONS_OK_CANCEL,
5498 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5500 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5501 gtk_widget_destroy ( dialog );
5502 if (response != GTK_RESPONSE_OK )
5505 /* Select engine from dialog */
5506 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5507 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5508 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5510 GTK_RESPONSE_REJECT,
5512 GTK_RESPONSE_ACCEPT,
5514 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5515 gtk_widget_show_all(label);
5517 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5519 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5520 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5521 gtk_widget_show_all(combo);
5523 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5525 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5527 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5529 /* Dialog validated: retrieve selected engine and do the job */
5530 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5531 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5534 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5536 /* Force saving track */
5537 /* FIXME: remove or rename this hack */
5538 vtl->route_finder_check_added_track = TRUE;
5541 vik_routing_engine_refine (routing, vtl, trk);
5543 /* FIXME: remove or rename this hack */
5544 if ( vtl->route_finder_added_track )
5545 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5547 vtl->route_finder_added_track = NULL;
5548 vtl->route_finder_check_added_track = FALSE;
5550 vik_layer_emit_update ( VIK_LAYER(vtl) );
5552 /* Restore cursor */
5553 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5555 gtk_widget_destroy ( dialog );
5559 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5561 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5562 trw_layer_tpwin_init ( vtl );
5565 /*************************************
5566 * merge/split by time routines
5567 *************************************/
5569 /* called for each key in track hash table.
5570 * If the current track has the same time stamp type, add it to the result,
5571 * except the one pointed by "exclude".
5572 * set exclude to NULL if there is no exclude to check.
5573 * Note that the result is in reverse (for performance reasons).
5578 gboolean with_timestamps;
5580 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5582 twt_udata *user_data = udata;
5583 VikTrackpoint *p1, *p2;
5584 VikTrack *trk = VIK_TRACK(value);
5585 if (trk == (VikTrack *)user_data->exclude) {
5589 if (trk->trackpoints) {
5590 p1 = vik_track_get_tp_first(trk);
5591 p2 = vik_track_get_tp_last(trk);
5593 if ( user_data->with_timestamps ) {
5594 if (!p1->has_timestamp || !p2->has_timestamp) {
5599 // Don't add tracks with timestamps when getting non timestamp tracks
5600 if (p1->has_timestamp || p2->has_timestamp) {
5606 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5609 /* called for each key in track hash table. if original track user_data[1] is close enough
5610 * to the passed one, add it to list in user_data[0]
5612 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5615 VikTrackpoint *p1, *p2;
5616 VikTrack *trk = VIK_TRACK(value);
5618 GList **nearby_tracks = ((gpointer *)user_data)[0];
5619 GList *tpoints = ((gpointer *)user_data)[1];
5622 * detect reasons for not merging, and return
5623 * if no reason is found not to merge, then do it.
5626 // Exclude the original track from the compiled list
5627 if (trk->trackpoints == tpoints) {
5631 t1 = vik_track_get_tp_first(trk)->timestamp;
5632 t2 = vik_track_get_tp_last(trk)->timestamp;
5634 if (trk->trackpoints) {
5635 p1 = vik_track_get_tp_first(trk);
5636 p2 = vik_track_get_tp_last(trk);
5638 if (!p1->has_timestamp || !p2->has_timestamp) {
5639 //g_print("no timestamp\n");
5643 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5644 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5645 if (! (abs(t1 - p2->timestamp) < threshold ||
5647 abs(p1->timestamp - t2) < threshold)
5654 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5657 /* comparison function used to sort tracks; a and b are hash table keys */
5658 /* Not actively used - can be restored if needed
5659 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5661 GHashTable *tracks = user_data;
5664 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5665 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5667 if (t1 < t2) return -1;
5668 if (t1 > t2) return 1;
5673 /* comparison function used to sort trackpoints */
5674 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5676 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5678 if (t1 < t2) return -1;
5679 if (t1 > t2) return 1;
5684 * comparison function which can be used to sort tracks or waypoints by name
5686 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5688 const gchar* namea = (const gchar*) a;
5689 const gchar* nameb = (const gchar*) b;
5690 if ( namea == NULL || nameb == NULL)
5693 // Same sort method as used in the vik_treeview_*_alphabetize functions
5694 return strcmp ( namea, nameb );
5698 * Attempt to merge selected track with other tracks specified by the user
5699 * Tracks to merge with must be of the same 'type' as the selected track -
5700 * either all with timestamps, or all without timestamps
5702 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5704 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5705 GList *other_tracks = NULL;
5706 GHashTable *ght_tracks;
5707 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5708 ght_tracks = vtl->routes;
5710 ght_tracks = vtl->tracks;
5712 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5717 if ( !track->trackpoints )
5721 udata.result = &other_tracks;
5722 udata.exclude = track->trackpoints;
5723 // Allow merging with 'similar' time type time tracks
5724 // i.e. either those times, or those without
5725 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5727 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5728 other_tracks = g_list_reverse(other_tracks);
5730 if ( !other_tracks ) {
5731 if ( udata.with_timestamps )
5732 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5734 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5738 // Sort alphabetically for user presentation
5739 // Convert into list of names for usage with dialog function
5740 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5741 GList *other_tracks_names = NULL;
5742 GList *iter = g_list_first ( other_tracks );
5744 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5745 iter = g_list_next ( iter );
5748 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5750 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5754 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5755 g_list_free(other_tracks);
5756 g_list_free(other_tracks_names);
5761 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5762 VikTrack *merge_track;
5763 if ( track->is_route )
5764 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5766 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5769 vik_track_steal_and_append_trackpoints ( track, merge_track );
5770 if ( track->is_route )
5771 vik_trw_layer_delete_route (vtl, merge_track);
5773 vik_trw_layer_delete_track (vtl, merge_track);
5774 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5777 for (l = merge_list; l != NULL; l = g_list_next(l))
5779 g_list_free(merge_list);
5781 vik_layer_emit_update( VIK_LAYER(vtl) );
5785 // c.f. trw_layer_sorted_track_id_by_name_list
5786 // but don't add the specified track to the list (normally current track)
5787 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5789 twt_udata *user_data = udata;
5792 if (trk->trackpoints == user_data->exclude) {
5796 // Sort named list alphabetically
5797 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5801 * Join - this allows combining 'tracks' and 'track routes'
5802 * i.e. doesn't care about whether tracks have consistent timestamps
5803 * ATM can only append one track at a time to the currently selected track
5805 static void trw_layer_append_track ( menu_array_sublayer values )
5808 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5810 GHashTable *ght_tracks;
5811 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5812 ght_tracks = vtl->routes;
5814 ght_tracks = vtl->tracks;
5816 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5821 GList *other_tracks_names = NULL;
5823 // Sort alphabetically for user presentation
5824 // Convert into list of names for usage with dialog function
5825 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5827 udata.result = &other_tracks_names;
5828 udata.exclude = trk->trackpoints;
5830 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5832 // Note the limit to selecting one track only
5833 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5834 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5835 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5838 trk->is_route ? _("Append Route"): _("Append Track"),
5839 trk->is_route ? _("Select the route to append after the current route") :
5840 _("Select the track to append after the current track") );
5842 g_list_free(other_tracks_names);
5844 // It's a list, but shouldn't contain more than one other track!
5845 if ( append_list ) {
5847 for (l = append_list; l != NULL; l = g_list_next(l)) {
5848 // TODO: at present this uses the first track found by name,
5849 // which with potential multiple same named tracks may not be the one selected...
5850 VikTrack *append_track;
5851 if ( trk->is_route )
5852 append_track = vik_trw_layer_get_route ( vtl, l->data );
5854 append_track = vik_trw_layer_get_track ( vtl, l->data );
5856 if ( append_track ) {
5857 vik_track_steal_and_append_trackpoints ( trk, append_track );
5858 if ( trk->is_route )
5859 vik_trw_layer_delete_route (vtl, append_track);
5861 vik_trw_layer_delete_track (vtl, append_track);
5864 for (l = append_list; l != NULL; l = g_list_next(l))
5866 g_list_free(append_list);
5868 vik_layer_emit_update( VIK_LAYER(vtl) );
5873 * Very similar to trw_layer_append_track for joining
5874 * but this allows selection from the 'other' list
5875 * If a track is selected, then is shows routes and joins the selected one
5876 * If a route is selected, then is shows tracks and joins the selected one
5878 static void trw_layer_append_other ( menu_array_sublayer values )
5881 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5883 GHashTable *ght_mykind, *ght_others;
5884 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5885 ght_mykind = vtl->routes;
5886 ght_others = vtl->tracks;
5889 ght_mykind = vtl->tracks;
5890 ght_others = vtl->routes;
5893 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5898 GList *other_tracks_names = NULL;
5900 // Sort alphabetically for user presentation
5901 // Convert into list of names for usage with dialog function
5902 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5904 udata.result = &other_tracks_names;
5905 udata.exclude = trk->trackpoints;
5907 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5909 // Note the limit to selecting one track only
5910 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5911 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5912 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5915 trk->is_route ? _("Append Track"): _("Append Route"),
5916 trk->is_route ? _("Select the track to append after the current route") :
5917 _("Select the route to append after the current track") );
5919 g_list_free(other_tracks_names);
5921 // It's a list, but shouldn't contain more than one other track!
5922 if ( append_list ) {
5924 for (l = append_list; l != NULL; l = g_list_next(l)) {
5925 // TODO: at present this uses the first track found by name,
5926 // which with potential multiple same named tracks may not be the one selected...
5928 // Get FROM THE OTHER TYPE list
5929 VikTrack *append_track;
5930 if ( trk->is_route )
5931 append_track = vik_trw_layer_get_track ( vtl, l->data );
5933 append_track = vik_trw_layer_get_route ( vtl, l->data );
5935 if ( append_track ) {
5937 if ( !append_track->is_route &&
5938 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5939 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5941 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5942 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5943 vik_track_merge_segments ( append_track );
5944 vik_track_to_routepoints ( append_track );
5951 vik_track_steal_and_append_trackpoints ( trk, append_track );
5953 // Delete copied which is FROM THE OTHER TYPE list
5954 if ( trk->is_route )
5955 vik_trw_layer_delete_track (vtl, append_track);
5957 vik_trw_layer_delete_route (vtl, append_track);
5960 for (l = append_list; l != NULL; l = g_list_next(l))
5962 g_list_free(append_list);
5963 vik_layer_emit_update( VIK_LAYER(vtl) );
5967 /* merge by segments */
5968 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5970 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5971 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5972 guint segments = vik_track_merge_segments ( trk );
5973 // NB currently no need to redraw as segments not actually shown on the display
5974 // However inform the user of what happened:
5976 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5977 g_snprintf(str, 64, tmp_str, segments);
5978 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5981 /* merge by time routine */
5982 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5984 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5988 GList *tracks_with_timestamp = NULL;
5989 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5990 if (orig_trk->trackpoints &&
5991 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
5992 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5997 udata.result = &tracks_with_timestamp;
5998 udata.exclude = orig_trk->trackpoints;
5999 udata.with_timestamps = TRUE;
6000 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6001 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6003 if (!tracks_with_timestamp) {
6004 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6007 g_list_free(tracks_with_timestamp);
6009 static guint threshold_in_minutes = 1;
6010 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6011 _("Merge Threshold..."),
6012 _("Merge when time between tracks less than:"),
6013 &threshold_in_minutes)) {
6017 // keep attempting to merge all tracks until no merges within the time specified is possible
6018 gboolean attempt_merge = TRUE;
6019 GList *nearby_tracks = NULL;
6021 static gpointer params[3];
6023 while ( attempt_merge ) {
6025 // Don't try again unless tracks have changed
6026 attempt_merge = FALSE;
6028 trps = orig_trk->trackpoints;
6032 if (nearby_tracks) {
6033 g_list_free(nearby_tracks);
6034 nearby_tracks = NULL;
6037 params[0] = &nearby_tracks;
6038 params[1] = (gpointer)trps;
6039 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6041 /* get a list of adjacent-in-time tracks */
6042 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6045 GList *l = nearby_tracks;
6047 /* remove trackpoints from merged track, delete track */
6048 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6049 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6051 // Tracks have changed, therefore retry again against all the remaining tracks
6052 attempt_merge = TRUE;
6057 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6060 g_list_free(nearby_tracks);
6062 vik_layer_emit_update( VIK_LAYER(vtl) );
6066 * Split a track at the currently selected trackpoint
6068 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6070 if ( !vtl->current_tpl )
6073 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6074 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6076 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6077 GList *newglist = g_list_alloc ();
6078 newglist->prev = NULL;
6079 newglist->next = vtl->current_tpl->next;
6080 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6081 tr->trackpoints = newglist;
6083 vtl->current_tpl->next->prev = newglist; /* end old track here */
6084 vtl->current_tpl->next = NULL;
6086 // Bounds of the selected track changed due to the split
6087 vik_track_calculate_bounds ( vtl->current_tp_track );
6089 vtl->current_tpl = newglist; /* change tp to first of new track. */
6090 vtl->current_tp_track = tr;
6093 vik_trw_layer_add_route ( vtl, name, tr );
6095 vik_trw_layer_add_track ( vtl, name, tr );
6097 // Bounds of the new track created by the split
6098 vik_track_calculate_bounds ( tr );
6104 // Also need id of newly created track
6107 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6109 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6111 if ( trkf && udata.uuid )
6112 vtl->current_tp_id = udata.uuid;
6114 vtl->current_tp_id = NULL;
6116 vik_layer_emit_update(VIK_LAYER(vtl));
6122 /* split by time routine */
6123 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6125 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6126 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6127 GList *trps = track->trackpoints;
6129 GList *newlists = NULL;
6130 GList *newtps = NULL;
6131 static guint thr = 1;
6138 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6139 _("Split Threshold..."),
6140 _("Split when time between trackpoints exceeds:"),
6145 /* iterate through trackpoints, and copy them into new lists without touching original list */
6146 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6150 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6152 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6155 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6156 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6157 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6159 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6164 if (ts - prev_ts > thr*60) {
6165 /* flush accumulated trackpoints into new list */
6166 newlists = g_list_append(newlists, g_list_reverse(newtps));
6170 /* accumulate trackpoint copies in newtps, in reverse order */
6171 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6173 iter = g_list_next(iter);
6176 newlists = g_list_append(newlists, g_list_reverse(newtps));
6179 /* put lists of trackpoints into tracks */
6181 // Only bother updating if the split results in new tracks
6182 if (g_list_length (newlists) > 1) {
6187 tr = vik_track_copy ( track, FALSE );
6188 tr->trackpoints = (GList *)(iter->data);
6190 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6191 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6192 g_free ( new_tr_name );
6193 vik_track_calculate_bounds ( tr );
6194 iter = g_list_next(iter);
6196 // Remove original track and then update the display
6197 vik_trw_layer_delete_track (vtl, track);
6198 vik_layer_emit_update(VIK_LAYER(vtl));
6200 g_list_free(newlists);
6204 * Split a track by the number of points as specified by the user
6206 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6208 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6210 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6211 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6213 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6218 // Check valid track
6219 GList *trps = track->trackpoints;
6223 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6224 _("Split Every Nth Point"),
6225 _("Split on every Nth point:"),
6226 250, // Default value as per typical limited track capacity of various GPS devices
6230 // Was a valid number returned?
6236 GList *newlists = NULL;
6237 GList *newtps = NULL;
6242 /* accumulate trackpoint copies in newtps, in reverse order */
6243 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6245 if (count >= points) {
6246 /* flush accumulated trackpoints into new list */
6247 newlists = g_list_append(newlists, g_list_reverse(newtps));
6251 iter = g_list_next(iter);
6254 // If there is a remaining chunk put that into the new split list
6255 // This may well be the whole track if no split points were encountered
6257 newlists = g_list_append(newlists, g_list_reverse(newtps));
6260 /* put lists of trackpoints into tracks */
6262 // Only bother updating if the split results in new tracks
6263 if (g_list_length (newlists) > 1) {
6268 tr = vik_track_copy ( track, FALSE );
6269 tr->trackpoints = (GList *)(iter->data);
6271 if ( track->is_route ) {
6272 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6273 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6276 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6277 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6279 g_free ( new_tr_name );
6280 vik_track_calculate_bounds ( tr );
6282 iter = g_list_next(iter);
6284 // Remove original track and then update the display
6285 if ( track->is_route )
6286 vik_trw_layer_delete_route (vtl, track);
6288 vik_trw_layer_delete_track (vtl, track);
6289 vik_layer_emit_update(VIK_LAYER(vtl));
6291 g_list_free(newlists);
6295 * Split a track at the currently selected trackpoint
6297 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6299 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6300 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6301 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6305 * Split a track by its segments
6306 * Routes do not have segments so don't call this for routes
6308 static void trw_layer_split_segments ( menu_array_sublayer values )
6310 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6311 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6318 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6321 for ( i = 0; i < ntracks; i++ ) {
6323 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6324 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6325 g_free ( new_tr_name );
6330 // Remove original track
6331 vik_trw_layer_delete_track ( vtl, trk );
6332 vik_layer_emit_update ( VIK_LAYER(vtl) );
6335 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6338 /* end of split/merge routines */
6340 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6344 // Find available adjacent trackpoint
6345 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6346 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6347 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6349 // Delete current trackpoint
6350 vik_trackpoint_free ( vtl->current_tpl->data );
6351 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6353 // Set to current to the available adjacent trackpoint
6354 vtl->current_tpl = new_tpl;
6356 if ( vtl->current_tp_track ) {
6357 vik_track_calculate_bounds ( vtl->current_tp_track );
6361 // Delete current trackpoint
6362 vik_trackpoint_free ( vtl->current_tpl->data );
6363 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6364 trw_layer_cancel_current_tp ( vtl, FALSE );
6369 * Delete the selected point
6371 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6373 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6375 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6376 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6378 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6383 if ( !vtl->current_tpl )
6386 trw_layer_trackpoint_selected_delete ( vtl, trk );
6388 // Track has been updated so update tps:
6389 trw_layer_cancel_tps_of_track ( vtl, trk );
6391 vik_layer_emit_update ( VIK_LAYER(vtl) );
6395 * Delete adjacent track points at the same position
6396 * AKA Delete Dulplicates on the Properties Window
6398 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6400 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6402 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6403 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6405 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6410 gulong removed = vik_track_remove_dup_points ( trk );
6412 // Track has been updated so update tps:
6413 trw_layer_cancel_tps_of_track ( vtl, trk );
6415 // Inform user how much was deleted as it's not obvious from the normal view
6417 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6418 g_snprintf(str, 64, tmp_str, removed);
6419 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6421 vik_layer_emit_update ( VIK_LAYER(vtl) );
6425 * Delete adjacent track points with the same timestamp
6426 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6428 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6430 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6432 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6433 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6435 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6440 gulong removed = vik_track_remove_same_time_points ( trk );
6442 // Track has been updated so update tps:
6443 trw_layer_cancel_tps_of_track ( vtl, trk );
6445 // Inform user how much was deleted as it's not obvious from the normal view
6447 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6448 g_snprintf(str, 64, tmp_str, removed);
6449 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6451 vik_layer_emit_update ( VIK_LAYER(vtl) );
6457 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6459 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6461 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6462 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6464 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6469 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6471 vik_layer_emit_update ( VIK_LAYER(vtl) );
6474 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6476 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6478 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6479 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6481 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6486 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6488 vik_layer_emit_update ( VIK_LAYER(vtl) );
6494 static void trw_layer_reverse ( menu_array_sublayer values )
6496 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6498 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6499 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6501 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6506 vik_track_reverse ( track );
6508 vik_layer_emit_update ( VIK_LAYER(vtl) );
6512 * Similar to trw_layer_enum_item, but this uses a sorted method
6515 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6517 GList **list = (GList**)udata;
6518 // *list = g_list_prepend(*all, key); //unsorted method
6519 // Sort named list alphabetically
6520 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6525 * Now Waypoint specific sort
6527 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6529 GList **list = (GList**)udata;
6530 // Sort named list alphabetically
6531 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6535 * Track specific sort
6537 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6539 GList **list = (GList**)udata;
6540 // Sort named list alphabetically
6541 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6546 gboolean has_same_track_name;
6547 const gchar *same_track_name;
6548 } same_track_name_udata;
6550 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6552 const gchar* namea = (const gchar*) aa;
6553 const gchar* nameb = (const gchar*) bb;
6556 gint result = strcmp ( namea, nameb );
6558 if ( result == 0 ) {
6559 // Found two names the same
6560 same_track_name_udata *user_data = udata;
6561 user_data->has_same_track_name = TRUE;
6562 user_data->same_track_name = namea;
6565 // Leave ordering the same
6570 * Find out if any tracks have the same name in this hash table
6572 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6574 // Sort items by name, then compare if any next to each other are the same
6576 GList *track_names = NULL;
6577 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6580 if ( ! track_names )
6583 same_track_name_udata udata;
6584 udata.has_same_track_name = FALSE;
6586 // Use sort routine to traverse list comparing items
6587 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6588 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6589 // Still no tracks...
6593 return udata.has_same_track_name;
6597 * Force unqiue track names for the track table specified
6598 * Note the panel is a required parameter to enable the update of the names displayed
6599 * Specify if on tracks or else on routes
6601 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6603 // . Search list for an instance of repeated name
6604 // . get track of this name
6605 // . create new name
6606 // . rename track & update equiv. treeview iter
6607 // . repeat until all different
6609 same_track_name_udata udata;
6611 GList *track_names = NULL;
6612 udata.has_same_track_name = FALSE;
6613 udata.same_track_name = NULL;
6615 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6618 if ( ! track_names )
6621 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6623 // Still no tracks...
6624 if ( ! dummy_list1 )
6627 while ( udata.has_same_track_name ) {
6629 // Find a track with the same name
6632 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6634 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6638 g_critical("Houston, we've had a problem.");
6639 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6640 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6645 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6646 vik_track_set_name ( trk, newname );
6652 // Need want key of it for treeview update
6653 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6655 if ( trkf && udataU.uuid ) {
6659 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6661 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6664 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6666 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6668 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6672 // Start trying to find same names again...
6674 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6675 udata.has_same_track_name = FALSE;
6676 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6678 // No tracks any more - give up searching
6679 if ( ! dummy_list2 )
6680 udata.has_same_track_name = FALSE;
6684 vik_layers_panel_emit_update ( vlp );
6687 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6689 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6692 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6693 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6694 iter = &(vtl->tracks_iter);
6695 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6697 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6698 iter = &(vtl->routes_iter);
6699 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6701 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6702 iter = &(vtl->waypoints_iter);
6703 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6707 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6710 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6712 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6715 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6716 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6717 iter = &(vtl->tracks_iter);
6718 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6720 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6721 iter = &(vtl->routes_iter);
6722 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6724 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6725 iter = &(vtl->waypoints_iter);
6726 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6730 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6736 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6738 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6741 // Ensure list of track names offered is unique
6742 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6743 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6744 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6745 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6751 // Sort list alphabetically for better presentation
6752 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6755 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6759 // Get list of items to delete from the user
6760 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6763 _("Delete Selection"),
6764 _("Select tracks to delete"));
6767 // Delete requested tracks
6768 // since specificly requested, IMHO no need for extra confirmation
6769 if ( delete_list ) {
6771 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6772 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6773 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6775 g_list_free(delete_list);
6776 vik_layer_emit_update( VIK_LAYER(vtl) );
6783 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6785 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6788 // Ensure list of track names offered is unique
6789 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6790 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6791 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6792 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6798 // Sort list alphabetically for better presentation
6799 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6802 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6806 // Get list of items to delete from the user
6807 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6810 _("Delete Selection"),
6811 _("Select routes to delete") );
6814 // Delete requested routes
6815 // since specificly requested, IMHO no need for extra confirmation
6816 if ( delete_list ) {
6818 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6819 // This deletes first route it finds of that name (but uniqueness is enforced above)
6820 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6822 g_list_free(delete_list);
6823 vik_layer_emit_update( VIK_LAYER(vtl) );
6828 gboolean has_same_waypoint_name;
6829 const gchar *same_waypoint_name;
6830 } same_waypoint_name_udata;
6832 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6834 const gchar* namea = (const gchar*) aa;
6835 const gchar* nameb = (const gchar*) bb;
6838 gint result = strcmp ( namea, nameb );
6840 if ( result == 0 ) {
6841 // Found two names the same
6842 same_waypoint_name_udata *user_data = udata;
6843 user_data->has_same_waypoint_name = TRUE;
6844 user_data->same_waypoint_name = namea;
6847 // Leave ordering the same
6852 * Find out if any waypoints have the same name in this layer
6854 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6856 // Sort items by name, then compare if any next to each other are the same
6858 GList *waypoint_names = NULL;
6859 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6862 if ( ! waypoint_names )
6865 same_waypoint_name_udata udata;
6866 udata.has_same_waypoint_name = FALSE;
6868 // Use sort routine to traverse list comparing items
6869 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6870 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6871 // Still no waypoints...
6875 return udata.has_same_waypoint_name;
6879 * Force unqiue waypoint names for this layer
6880 * Note the panel is a required parameter to enable the update of the names displayed
6882 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6884 // . Search list for an instance of repeated name
6885 // . get waypoint of this name
6886 // . create new name
6887 // . rename waypoint & update equiv. treeview iter
6888 // . repeat until all different
6890 same_waypoint_name_udata udata;
6892 GList *waypoint_names = NULL;
6893 udata.has_same_waypoint_name = FALSE;
6894 udata.same_waypoint_name = NULL;
6896 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6899 if ( ! waypoint_names )
6902 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6904 // Still no waypoints...
6905 if ( ! dummy_list1 )
6908 while ( udata.has_same_waypoint_name ) {
6910 // Find a waypoint with the same name
6911 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6915 g_critical("Houston, we've had a problem.");
6916 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6917 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6922 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6924 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6926 // Start trying to find same names again...
6927 waypoint_names = NULL;
6928 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6929 udata.has_same_waypoint_name = FALSE;
6930 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6932 // No waypoints any more - give up searching
6933 if ( ! dummy_list2 )
6934 udata.has_same_waypoint_name = FALSE;
6938 vik_layers_panel_emit_update ( vlp );
6944 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6946 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6949 // Ensure list of waypoint names offered is unique
6950 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6951 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6952 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6953 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6959 // Sort list alphabetically for better presentation
6960 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6962 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6966 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6968 // Get list of items to delete from the user
6969 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6972 _("Delete Selection"),
6973 _("Select waypoints to delete"));
6976 // Delete requested waypoints
6977 // since specificly requested, IMHO no need for extra confirmation
6978 if ( delete_list ) {
6980 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6981 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6982 trw_layer_delete_waypoint_by_name (vtl, l->data);
6984 g_list_free(delete_list);
6986 trw_layer_calculate_bounds_waypoints ( vtl );
6987 vik_layer_emit_update( VIK_LAYER(vtl) );
6995 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6997 vik_treeview_item_toggle_visible ( vt, it );
7003 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7005 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7011 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7013 wp->visible = GPOINTER_TO_INT (on_off);
7019 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7021 wp->visible = !wp->visible;
7027 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7029 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7030 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7031 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7032 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7034 vik_layer_emit_update ( VIK_LAYER(vtl) );
7040 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7042 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7043 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7044 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7045 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7047 vik_layer_emit_update ( VIK_LAYER(vtl) );
7053 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7055 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7056 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7057 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7059 vik_layer_emit_update ( VIK_LAYER(vtl) );
7065 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7067 trk->visible = GPOINTER_TO_INT (on_off);
7073 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7075 trk->visible = !trk->visible;
7081 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7083 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7084 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7085 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7086 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7088 vik_layer_emit_update ( VIK_LAYER(vtl) );
7094 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7096 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7097 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7098 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7099 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7101 vik_layer_emit_update ( VIK_LAYER(vtl) );
7107 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7109 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7110 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7111 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7113 vik_layer_emit_update ( VIK_LAYER(vtl) );
7119 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7121 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7122 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7123 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7124 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7126 vik_layer_emit_update ( VIK_LAYER(vtl) );
7132 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7134 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7135 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7136 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7137 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7139 vik_layer_emit_update ( VIK_LAYER(vtl) );
7145 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7147 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7148 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7149 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7151 vik_layer_emit_update ( VIK_LAYER(vtl) );
7155 * vik_trw_layer_build_waypoint_list_t:
7157 * Helper function to construct a list of #vik_trw_waypoint_list_t
7159 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7161 GList *waypoints_and_layers = NULL;
7162 // build waypoints_and_layers list
7163 while ( waypoints ) {
7164 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7165 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7167 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7168 waypoints = g_list_next ( waypoints );
7170 return waypoints_and_layers;
7174 * trw_layer_create_waypoint_list:
7176 * Create the latest list of waypoints with the associated layer(s)
7177 * Although this will always be from a single layer here
7179 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7181 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7182 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7184 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7188 * trw_layer_analyse_close:
7190 * Stuff to do on dialog closure
7192 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7194 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7195 gtk_widget_destroy ( dialog );
7196 vtl->tracks_analysis_dialog = NULL;
7200 * vik_trw_layer_build_track_list_t:
7202 * Helper function to construct a list of #vik_trw_track_list_t
7204 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7206 GList *tracks_and_layers = NULL;
7207 // build tracks_and_layers list
7209 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7210 vtdl->trk = VIK_TRACK(tracks->data);
7212 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7213 tracks = g_list_next ( tracks );
7215 return tracks_and_layers;
7219 * trw_layer_create_track_list:
7221 * Create the latest list of tracks with the associated layer(s)
7222 * Although this will always be from a single layer here
7224 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7226 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7227 GList *tracks = NULL;
7228 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7229 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7231 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7233 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7236 static void trw_layer_tracks_stats ( menu_array_layer values )
7238 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7239 // There can only be one!
7240 if ( vtl->tracks_analysis_dialog )
7243 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7244 VIK_LAYER(vtl)->name,
7246 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7247 trw_layer_create_track_list,
7248 trw_layer_analyse_close );
7254 static void trw_layer_routes_stats ( menu_array_layer values )
7256 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7257 // There can only be one!
7258 if ( vtl->tracks_analysis_dialog )
7261 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7262 VIK_LAYER(vtl)->name,
7264 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7265 trw_layer_create_track_list,
7266 trw_layer_analyse_close );
7269 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7271 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7272 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7274 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7277 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7279 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7280 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7283 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7284 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7288 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7290 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7291 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7294 if ( !strncmp(wp->comment, "http", 4) ) {
7295 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7296 } else if ( !strncmp(wp->description, "http", 4) ) {
7297 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7301 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7303 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7305 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7307 // No actual change to the name supplied
7309 if (strcmp(newname, wp->name) == 0 )
7312 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7315 // An existing waypoint has been found with the requested name
7316 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7317 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7322 // Update WP name and refresh the treeview
7323 vik_waypoint_set_name (wp, newname);
7325 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7326 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7328 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7333 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7335 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7337 // No actual change to the name supplied
7339 if (strcmp(newname, trk->name) == 0)
7342 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7345 // An existing track has been found with the requested name
7346 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7347 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7351 // Update track name and refresh GUI parts
7352 vik_track_set_name (trk, newname);
7354 // Update any subwindows that could be displaying this track which has changed name
7355 // Only one Track Edit Window
7356 if ( l->current_tp_track == trk && l->tpwin ) {
7357 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7359 // Property Dialog of the track
7360 vik_trw_layer_propwin_update ( trk );
7362 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7363 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7365 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7370 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7372 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7374 // No actual change to the name supplied
7376 if (strcmp(newname, trk->name) == 0)
7379 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7382 // An existing track has been found with the requested name
7383 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7384 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7388 // Update track name and refresh GUI parts
7389 vik_track_set_name (trk, newname);
7391 // Update any subwindows that could be displaying this track which has changed name
7392 // Only one Track Edit Window
7393 if ( l->current_tp_track == trk && l->tpwin ) {
7394 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7396 // Property Dialog of the track
7397 vik_trw_layer_propwin_update ( trk );
7399 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7400 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7402 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7409 static gboolean is_valid_geocache_name ( gchar *str )
7411 gint len = strlen ( str );
7412 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]));
7415 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7417 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7418 a_acquire_set_filter_track ( trk );
7421 #ifdef VIK_CONFIG_GOOGLE
7422 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7424 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7425 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7428 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7430 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7432 gchar *escaped = uri_escape ( tr->comment );
7433 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7434 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7441 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7442 /* viewpoint is now available instead */
7443 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7445 static menu_array_sublayer pass_along;
7447 gboolean rv = FALSE;
7449 pass_along[MA_VTL] = l;
7450 pass_along[MA_VLP] = vlp;
7451 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7452 pass_along[MA_SUBLAYER_ID] = sublayer;
7453 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7454 pass_along[MA_VVP] = vvp;
7455 pass_along[MA_TV_ITER] = iter;
7456 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7458 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7462 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7463 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7464 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7465 gtk_widget_show ( item );
7467 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7468 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7469 if (tr && tr->property_dialog)
7470 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7472 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7473 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7474 if (tr && tr->property_dialog)
7475 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7478 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7480 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7481 gtk_widget_show ( item );
7483 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7484 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7485 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7486 gtk_widget_show ( item );
7488 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7490 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7491 gtk_widget_show ( item );
7493 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7495 // Always create separator as now there is always at least the transform menu option
7496 item = gtk_menu_item_new ();
7497 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7498 gtk_widget_show ( item );
7500 /* could be a right-click using the tool */
7501 if ( vlp != NULL ) {
7502 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7505 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7506 gtk_widget_show ( item );
7509 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7511 if ( wp && wp->name ) {
7512 if ( is_valid_geocache_name ( wp->name ) ) {
7513 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7515 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7516 gtk_widget_show ( item );
7518 #ifdef VIK_CONFIG_GEOTAG
7519 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7520 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7521 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7522 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7523 gtk_widget_show ( item );
7527 if ( wp && wp->image )
7529 // Set up image paramater
7530 pass_along[MA_MISC] = wp->image;
7532 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7533 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
7534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7535 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7536 gtk_widget_show ( item );
7538 #ifdef VIK_CONFIG_GEOTAG
7539 GtkWidget *geotag_submenu = gtk_menu_new ();
7540 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7541 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7542 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7543 gtk_widget_show ( item );
7544 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7546 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7547 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7548 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7549 gtk_widget_show ( item );
7551 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7552 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7553 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7554 gtk_widget_show ( item );
7560 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7561 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7562 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7563 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7564 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7565 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7566 gtk_widget_show ( item );
7572 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7573 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7574 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7575 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7576 gtk_widget_show ( item );
7577 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7578 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7579 gtk_widget_set_sensitive ( item, TRUE );
7581 gtk_widget_set_sensitive ( item, FALSE );
7584 item = gtk_menu_item_new ();
7585 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7586 gtk_widget_show ( item );
7589 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7592 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7593 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7595 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7596 gtk_widget_show ( item );
7599 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7601 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7604 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7605 gtk_widget_show ( item );
7607 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7610 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7611 gtk_widget_show ( item );
7613 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7614 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7616 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7617 gtk_widget_show ( item );
7619 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7620 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7621 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7622 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7623 gtk_widget_show ( item );
7625 GtkWidget *vis_submenu = gtk_menu_new ();
7626 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7627 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7628 gtk_widget_show ( item );
7629 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7631 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7632 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7633 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7634 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7635 gtk_widget_show ( item );
7637 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7638 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7640 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7641 gtk_widget_show ( item );
7643 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7644 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7645 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7646 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7647 gtk_widget_show ( item );
7649 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7650 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7651 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7652 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7655 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7659 if ( l->current_track && !l->current_track->is_route ) {
7660 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7662 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7663 gtk_widget_show ( item );
7665 item = gtk_menu_item_new ();
7666 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7667 gtk_widget_show ( item );
7670 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7671 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7672 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7673 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7674 gtk_widget_show ( item );
7676 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7677 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7679 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7680 gtk_widget_show ( item );
7681 // Make it available only when a new track *not* already in progress
7682 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7684 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7685 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7686 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7687 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7688 gtk_widget_show ( item );
7690 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7691 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7693 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7694 gtk_widget_show ( item );
7696 GtkWidget *vis_submenu = gtk_menu_new ();
7697 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7698 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7699 gtk_widget_show ( item );
7700 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7702 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7705 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7706 gtk_widget_show ( item );
7708 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7709 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7711 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7712 gtk_widget_show ( item );
7714 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7715 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7716 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7717 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7719 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7722 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7723 gtk_widget_show ( item );
7725 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7726 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7727 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7728 gtk_widget_show ( item );
7731 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7735 if ( l->current_track && l->current_track->is_route ) {
7736 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7737 // Reuse finish track method
7738 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7739 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7740 gtk_widget_show ( item );
7742 item = gtk_menu_item_new ();
7743 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7744 gtk_widget_show ( item );
7747 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7748 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7749 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7750 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7751 gtk_widget_show ( item );
7753 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7754 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7756 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7757 gtk_widget_show ( item );
7758 // Make it available only when a new track *not* already in progress
7759 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7761 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7764 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7765 gtk_widget_show ( item );
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7770 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7771 gtk_widget_show ( item );
7773 GtkWidget *vis_submenu = gtk_menu_new ();
7774 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7775 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7776 gtk_widget_show ( item );
7777 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7779 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7780 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7782 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7783 gtk_widget_show ( item );
7785 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7788 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7789 gtk_widget_show ( item );
7791 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7794 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7796 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7797 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7798 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7799 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7801 gtk_widget_show ( item );
7803 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7805 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7806 gtk_widget_show ( item );
7810 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7811 GtkWidget *submenu_sort = gtk_menu_new ();
7812 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7814 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7815 gtk_widget_show ( item );
7816 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7818 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7819 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7820 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7821 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7822 gtk_widget_show ( item );
7824 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7825 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7827 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7828 gtk_widget_show ( item );
7831 GtkWidget *upload_submenu = gtk_menu_new ();
7833 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7835 item = gtk_menu_item_new ();
7836 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7837 gtk_widget_show ( item );
7839 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7840 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7841 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7842 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7843 if ( l->current_track ) {
7844 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7845 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7846 gtk_widget_show ( item );
7849 item = gtk_menu_item_new ();
7850 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7851 gtk_widget_show ( item );
7854 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7855 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7857 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7858 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7859 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7860 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7861 gtk_widget_show ( item );
7863 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7865 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7866 gtk_widget_show ( item );
7868 GtkWidget *goto_submenu;
7869 goto_submenu = gtk_menu_new ();
7870 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7871 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7872 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7873 gtk_widget_show ( item );
7874 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7876 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7877 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7878 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7879 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7880 gtk_widget_show ( item );
7882 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7883 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7884 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7885 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7886 gtk_widget_show ( item );
7888 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7889 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7890 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7891 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7892 gtk_widget_show ( item );
7894 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7895 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7897 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7898 gtk_widget_show ( item );
7900 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7901 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7904 gtk_widget_show ( item );
7906 // Routes don't have speeds
7907 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7908 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7909 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7911 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7912 gtk_widget_show ( item );
7915 GtkWidget *combine_submenu;
7916 combine_submenu = gtk_menu_new ();
7917 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7918 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7919 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7920 gtk_widget_show ( item );
7921 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7923 // Routes don't have times or segments...
7924 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7925 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7927 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7928 gtk_widget_show ( item );
7930 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7931 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7932 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7933 gtk_widget_show ( item );
7936 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7937 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7938 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7939 gtk_widget_show ( item );
7941 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7942 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7944 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7947 gtk_widget_show ( item );
7949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7950 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7952 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7954 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7955 gtk_widget_show ( item );
7957 GtkWidget *split_submenu;
7958 split_submenu = gtk_menu_new ();
7959 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7961 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7962 gtk_widget_show ( item );
7963 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7965 // Routes don't have times or segments...
7966 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7967 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7969 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7970 gtk_widget_show ( item );
7972 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7973 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7975 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7976 gtk_widget_show ( item );
7979 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7981 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7982 gtk_widget_show ( item );
7984 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7986 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7987 gtk_widget_show ( item );
7988 // Make it available only when a trackpoint is selected.
7989 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7991 GtkWidget *insert_submenu = gtk_menu_new ();
7992 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7993 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7994 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7995 gtk_widget_show ( item );
7996 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7998 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8001 gtk_widget_show ( item );
8002 // Make it available only when a point is selected
8003 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8005 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8007 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8008 gtk_widget_show ( item );
8009 // Make it available only when a point is selected
8010 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8012 GtkWidget *delete_submenu;
8013 delete_submenu = gtk_menu_new ();
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8016 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8017 gtk_widget_show ( item );
8018 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8020 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8023 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8024 gtk_widget_show ( item );
8025 // Make it available only when a point is selected
8026 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8028 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8030 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8031 gtk_widget_show ( item );
8033 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8035 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8036 gtk_widget_show ( item );
8038 GtkWidget *transform_submenu;
8039 transform_submenu = gtk_menu_new ();
8040 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8042 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8043 gtk_widget_show ( item );
8044 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8046 GtkWidget *dem_submenu;
8047 dem_submenu = gtk_menu_new ();
8048 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8049 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
8050 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8051 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8053 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8056 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8057 gtk_widget_show ( item );
8059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8061 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8062 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8063 gtk_widget_show ( item );
8065 GtkWidget *smooth_submenu;
8066 smooth_submenu = gtk_menu_new ();
8067 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8068 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8069 gtk_widget_show ( item );
8070 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8072 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8074 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8075 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8076 gtk_widget_show ( item );
8078 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8080 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8081 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8082 gtk_widget_show ( item );
8084 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8085 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8087 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8090 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8091 gtk_widget_show ( item );
8093 // Routes don't have timestamps - so this is only available for tracks
8094 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8095 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8096 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8097 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8098 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8099 gtk_widget_show ( item );
8102 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8105 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8108 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8109 gtk_widget_show ( item );
8111 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8112 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8113 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8115 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8116 gtk_widget_show ( item );
8119 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8121 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8122 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8124 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8125 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
8126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8127 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8128 gtk_widget_show ( item );
8131 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8132 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8134 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8137 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8138 gtk_widget_show ( item );
8140 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8141 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8143 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8144 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8145 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8146 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8147 gtk_widget_show ( item );
8149 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8150 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8151 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
8152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8153 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8154 gtk_widget_show ( item );
8157 // ATM can't upload a single waypoint but can do waypoints to a GPS
8158 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8159 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8160 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8161 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8162 gtk_widget_show ( item );
8163 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8165 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8167 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8168 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8169 gtk_widget_show ( item );
8173 #ifdef VIK_CONFIG_GOOGLE
8174 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8176 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8179 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8180 gtk_widget_show ( item );
8184 // Some things aren't usable with routes
8185 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8186 #ifdef VIK_CONFIG_OPENSTREETMAP
8187 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8188 // Convert internal pointer into track
8189 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8190 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8192 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8193 gtk_widget_show ( item );
8196 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8197 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8199 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8200 gtk_widget_show ( item );
8202 /* ATM This function is only available via the layers panel, due to needing a vlp */
8204 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8205 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8206 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8208 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8209 gtk_widget_show ( item );
8213 #ifdef VIK_CONFIG_GEOTAG
8214 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8216 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8217 gtk_widget_show ( item );
8221 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8222 // Only show on viewport popmenu when a trackpoint is selected
8223 if ( ! vlp && l->current_tpl ) {
8225 item = gtk_menu_item_new ();
8226 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8227 gtk_widget_show ( item );
8229 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8230 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8231 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8232 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8233 gtk_widget_show ( item );
8237 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8238 GtkWidget *transform_submenu;
8239 transform_submenu = gtk_menu_new ();
8240 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8242 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8243 gtk_widget_show ( item );
8244 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8246 GtkWidget *dem_submenu;
8247 dem_submenu = gtk_menu_new ();
8248 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8249 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
8250 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8251 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8253 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8255 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8256 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8257 gtk_widget_show ( item );
8259 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8261 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8262 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8263 gtk_widget_show ( item );
8266 gtk_widget_show_all ( GTK_WIDGET(menu) );
8271 // TODO: Probably better to rework this track manipulation in viktrack.c
8272 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8275 if (!vtl->current_tpl)
8278 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8279 VikTrackpoint *tp_other = NULL;
8282 if (!vtl->current_tpl->prev)
8284 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8286 if (!vtl->current_tpl->next)
8288 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8291 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8294 VikTrackpoint *tp_new = vik_trackpoint_new();
8295 struct LatLon ll_current, ll_other;
8296 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8297 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8299 /* main positional interpolation */
8300 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8301 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8303 /* Now other properties that can be interpolated */
8304 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8306 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8307 /* Note here the division is applied to each part, then added
8308 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8309 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8310 tp_new->has_timestamp = TRUE;
8313 if (tp_current->speed != NAN && tp_other->speed != NAN)
8314 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8316 /* TODO - improve interpolation of course, as it may not be correct.
8317 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8318 [similar applies if value is in radians] */
8319 if (tp_current->course != NAN && tp_other->course != NAN)
8320 tp_new->course = (tp_current->course + tp_other->course)/2;
8322 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8324 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8325 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8327 // Otherwise try routes
8328 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8332 gint index = g_list_index ( trk->trackpoints, tp_current );
8336 // NB no recalculation of bounds since it is inserted between points
8337 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8342 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8348 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8352 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8354 if ( vtl->current_tpl )
8356 vtl->current_tpl = NULL;
8357 vtl->current_tp_track = NULL;
8358 vtl->current_tp_id = NULL;
8359 vik_layer_emit_update(VIK_LAYER(vtl));
8363 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8365 g_assert ( vtl->tpwin != NULL );
8366 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8367 trw_layer_cancel_current_tp ( vtl, TRUE );
8369 if ( vtl->current_tpl == NULL )
8372 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8374 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8375 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8377 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8379 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8381 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8385 trw_layer_trackpoint_selected_delete ( vtl, tr );
8387 if ( vtl->current_tpl )
8388 // Reset dialog with the available adjacent trackpoint
8389 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8391 vik_layer_emit_update(VIK_LAYER(vtl));
8393 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8395 if ( vtl->current_tp_track )
8396 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8397 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8399 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8401 if ( vtl->current_tp_track )
8402 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8403 vik_layer_emit_update(VIK_LAYER(vtl));
8405 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8407 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8408 vik_layer_emit_update(VIK_LAYER(vtl));
8410 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8411 vik_layer_emit_update(VIK_LAYER(vtl));
8415 * trw_layer_dialog_shift:
8416 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8418 * Try to reposition a dialog if it's over the specified coord
8419 * so to not obscure the item of interest
8421 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8423 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8425 // Attempt force dialog to be shown so we can find out where it is more reliably...
8426 while ( gtk_events_pending() )
8427 gtk_main_iteration ();
8429 // get parent window position & size
8430 gint win_pos_x, win_pos_y;
8431 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8433 gint win_size_x, win_size_y;
8434 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8436 // get own dialog size
8437 gint dia_size_x, dia_size_y;
8438 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8440 // get own dialog position
8441 gint dia_pos_x, dia_pos_y;
8442 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8444 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8445 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8447 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8449 gint vp_xx, vp_yy; // In viewport pixels
8450 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8452 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8456 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8458 // Transform Viewport pixels into absolute pixels
8459 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8460 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8462 // Is dialog over the point (to within an ^^ edge value)
8463 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8464 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8468 gint hh = vik_viewport_get_height ( vvp );
8470 // Consider the difference in viewport to the full window
8471 gint offset_y = dest_y;
8472 // Add difference between dialog and window sizes
8473 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8475 if ( vp_yy > hh/2 ) {
8476 // Point in bottom half, move window to top half
8477 gtk_window_move ( dialog, dia_pos_x, offset_y );
8480 // Point in top half, move dialog down
8481 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8485 // Shift left<->right
8486 gint ww = vik_viewport_get_width ( vvp );
8488 // Consider the difference in viewport to the full window
8489 gint offset_x = dest_x;
8490 // Add difference between dialog and window sizes
8491 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8493 if ( vp_xx > ww/2 ) {
8494 // Point on right, move window to left
8495 gtk_window_move ( dialog, offset_x, dia_pos_y );
8498 // Point on left, move right
8499 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8507 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8511 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8512 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8513 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8514 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8516 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8518 if ( vtl->current_tpl ) {
8519 // get tp pixel position
8520 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8522 // Shift up<->down to try not to obscure the trackpoint.
8523 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8527 if ( vtl->current_tpl )
8528 if ( vtl->current_tp_track )
8529 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8530 /* set layer name and TP data */
8533 /***************************************************************************
8535 ***************************************************************************/
8537 /*** Utility data structures and functions ****/
8541 gint closest_x, closest_y;
8542 gboolean draw_images;
8543 gpointer *closest_wp_id;
8544 VikWaypoint *closest_wp;
8550 gint closest_x, closest_y;
8551 gpointer closest_track_id;
8552 VikTrackpoint *closest_tp;
8558 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8564 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8566 // If waypoint has an image then use the image size to select
8567 if ( params->draw_images && wp->image ) {
8568 gint slackx, slacky;
8569 slackx = wp->image_width / 2;
8570 slacky = wp->image_height / 2;
8572 if ( x <= params->x + slackx && x >= params->x - slackx
8573 && y <= params->y + slacky && y >= params->y - slacky ) {
8574 params->closest_wp_id = id;
8575 params->closest_wp = wp;
8576 params->closest_x = x;
8577 params->closest_y = y;
8580 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8581 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8582 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8584 params->closest_wp_id = id;
8585 params->closest_wp = wp;
8586 params->closest_x = x;
8587 params->closest_y = y;
8591 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8593 GList *tpl = t->trackpoints;
8599 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8605 tp = VIK_TRACKPOINT(tpl->data);
8607 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8609 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8610 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8611 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8613 params->closest_track_id = id;
8614 params->closest_tp = tp;
8615 params->closest_tpl = tpl;
8616 params->closest_x = x;
8617 params->closest_y = y;
8623 // ATM: Leave this as 'Track' only.
8624 // Not overly bothered about having a snap to route trackpoint capability
8625 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8627 TPSearchParams params;
8631 params.closest_track_id = NULL;
8632 params.closest_tp = NULL;
8633 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8634 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8635 return params.closest_tp;
8638 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8640 WPSearchParams params;
8644 params.draw_images = vtl->drawimages;
8645 params.closest_wp = NULL;
8646 params.closest_wp_id = NULL;
8647 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8648 return params.closest_wp;
8652 // Some forward declarations
8653 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8654 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8655 static void marker_end_move ( tool_ed_t *t );
8658 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8662 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8664 // Here always allow snapping back to the original location
8665 // this is useful when one decides not to move the thing afterall
8666 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8669 if ( event->state & GDK_CONTROL_MASK )
8671 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8673 new_coord = tp->coord;
8677 if ( event->state & GDK_SHIFT_MASK )
8679 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8681 new_coord = wp->coord;
8685 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8687 marker_moveto ( t, x, y );
8694 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8696 if ( t->holding && event->button == 1 )
8699 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8702 if ( event->state & GDK_CONTROL_MASK )
8704 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8706 new_coord = tp->coord;
8710 if ( event->state & GDK_SHIFT_MASK )
8712 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8714 new_coord = wp->coord;
8717 marker_end_move ( t );
8719 // Determine if working on a waypoint or a trackpoint
8720 if ( t->is_waypoint ) {
8721 // Update waypoint position
8722 vtl->current_wp->coord = new_coord;
8723 trw_layer_calculate_bounds_waypoints ( vtl );
8724 // Reset waypoint pointer
8725 vtl->current_wp = NULL;
8726 vtl->current_wp_id = NULL;
8729 if ( vtl->current_tpl ) {
8730 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8732 if ( vtl->current_tp_track )
8733 vik_track_calculate_bounds ( vtl->current_tp_track );
8736 if ( vtl->current_tp_track )
8737 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8738 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8742 vik_layer_emit_update ( VIK_LAYER(vtl) );
8749 Returns true if a waypoint or track is found near the requested event position for this particular layer
8750 The item found is automatically selected
8751 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8753 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8755 if ( event->button != 1 )
8758 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8761 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8765 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8767 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8769 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8770 WPSearchParams wp_params;
8771 wp_params.vvp = vvp;
8772 wp_params.x = event->x;
8773 wp_params.y = event->y;
8774 wp_params.draw_images = vtl->drawimages;
8775 wp_params.closest_wp_id = NULL;
8776 wp_params.closest_wp = NULL;
8778 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8780 if ( wp_params.closest_wp ) {
8783 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8785 // Too easy to move it so must be holding shift to start immediately moving it
8786 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8787 if ( event->state & GDK_SHIFT_MASK ||
8788 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8789 // Put into 'move buffer'
8790 // NB vvp & vw already set in tet
8791 tet->vtl = (gpointer)vtl;
8792 tet->is_waypoint = TRUE;
8794 marker_begin_move (tet, event->x, event->y);
8797 vtl->current_wp = wp_params.closest_wp;
8798 vtl->current_wp_id = wp_params.closest_wp_id;
8800 if ( event->type == GDK_2BUTTON_PRESS ) {
8801 if ( vtl->current_wp->image ) {
8802 menu_array_sublayer values;
8803 values[MA_VTL] = vtl;
8804 values[MA_MISC] = vtl->current_wp->image;
8805 trw_layer_show_picture ( values );
8809 vik_layer_emit_update ( VIK_LAYER(vtl) );
8815 // Used for both track and route lists
8816 TPSearchParams tp_params;
8817 tp_params.vvp = vvp;
8818 tp_params.x = event->x;
8819 tp_params.y = event->y;
8820 tp_params.closest_track_id = NULL;
8821 tp_params.closest_tp = NULL;
8822 tp_params.closest_tpl = NULL;
8823 tp_params.bbox = bbox;
8825 if (vtl->tracks_visible) {
8826 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8828 if ( tp_params.closest_tp ) {
8830 // Always select + highlight the track
8831 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8833 tet->is_waypoint = FALSE;
8835 // Select the Trackpoint
8836 // Can move it immediately when control held or it's the previously selected tp
8837 if ( event->state & GDK_CONTROL_MASK ||
8838 vtl->current_tpl == tp_params.closest_tpl ) {
8839 // Put into 'move buffer'
8840 // NB vvp & vw already set in tet
8841 tet->vtl = (gpointer)vtl;
8842 marker_begin_move (tet, event->x, event->y);
8845 vtl->current_tpl = tp_params.closest_tpl;
8846 vtl->current_tp_id = tp_params.closest_track_id;
8847 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8849 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8852 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8854 vik_layer_emit_update ( VIK_LAYER(vtl) );
8859 // Try again for routes
8860 if (vtl->routes_visible) {
8861 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8863 if ( tp_params.closest_tp ) {
8865 // Always select + highlight the track
8866 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8868 tet->is_waypoint = FALSE;
8870 // Select the Trackpoint
8871 // Can move it immediately when control held or it's the previously selected tp
8872 if ( event->state & GDK_CONTROL_MASK ||
8873 vtl->current_tpl == tp_params.closest_tpl ) {
8874 // Put into 'move buffer'
8875 // NB vvp & vw already set in tet
8876 tet->vtl = (gpointer)vtl;
8877 marker_begin_move (tet, event->x, event->y);
8880 vtl->current_tpl = tp_params.closest_tpl;
8881 vtl->current_tp_id = tp_params.closest_track_id;
8882 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8884 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8887 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8889 vik_layer_emit_update ( VIK_LAYER(vtl) );
8894 /* these aren't the droids you're looking for */
8895 vtl->current_wp = NULL;
8896 vtl->current_wp_id = NULL;
8897 trw_layer_cancel_current_tp ( vtl, FALSE );
8900 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8905 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8907 if ( event->button != 3 )
8910 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8913 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8916 /* Post menu for the currently selected item */
8918 /* See if a track is selected */
8919 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8920 if ( track && track->visible ) {
8922 if ( track->name ) {
8924 if ( vtl->track_right_click_menu )
8925 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8927 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8934 if ( track->is_route )
8935 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8937 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8939 if ( trkf && udataU.uuid ) {
8942 if ( track->is_route )
8943 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8945 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8947 trw_layer_sublayer_add_menu_items ( vtl,
8948 vtl->track_right_click_menu,
8950 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8956 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8962 /* See if a waypoint is selected */
8963 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8964 if ( waypoint && waypoint->visible ) {
8965 if ( waypoint->name ) {
8967 if ( vtl->wp_right_click_menu )
8968 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8970 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8973 udata.wp = waypoint;
8976 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8978 if ( wpf && udata.uuid ) {
8979 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8981 trw_layer_sublayer_add_menu_items ( vtl,
8982 vtl->wp_right_click_menu,
8984 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8989 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8998 /* background drawing hook, to be passed the viewport */
8999 static gboolean tool_sync_done = TRUE;
9001 static gboolean tool_sync(gpointer data)
9003 VikViewport *vvp = data;
9004 gdk_threads_enter();
9005 vik_viewport_sync(vvp);
9006 tool_sync_done = TRUE;
9007 gdk_threads_leave();
9011 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9014 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9015 gdk_gc_set_function ( t->gc, GDK_INVERT );
9016 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9017 vik_viewport_sync(t->vvp);
9022 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9024 VikViewport *vvp = t->vvp;
9025 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9026 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9030 if (tool_sync_done) {
9031 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9032 tool_sync_done = FALSE;
9036 static void marker_end_move ( tool_ed_t *t )
9038 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9039 g_object_unref ( t->gc );
9043 /*** Edit waypoint ****/
9045 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9047 tool_ed_t *t = g_new(tool_ed_t, 1);
9053 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9058 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9060 WPSearchParams params;
9061 tool_ed_t *t = data;
9062 VikViewport *vvp = t->vvp;
9064 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9071 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9074 if ( vtl->current_wp && vtl->current_wp->visible )
9076 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9078 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9080 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9081 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9083 if ( event->button == 3 )
9084 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9086 marker_begin_move(t, event->x, event->y);
9093 params.x = event->x;
9094 params.y = event->y;
9095 params.draw_images = vtl->drawimages;
9096 params.closest_wp_id = NULL;
9097 params.closest_wp = NULL;
9098 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9099 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9101 marker_begin_move(t, event->x, event->y);
9104 else if ( params.closest_wp )
9106 if ( event->button == 3 )
9107 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9109 vtl->waypoint_rightclick = FALSE;
9111 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9113 vtl->current_wp = params.closest_wp;
9114 vtl->current_wp_id = params.closest_wp_id;
9116 /* could make it so don't update if old WP is off screen and new is null but oh well */
9117 vik_layer_emit_update ( VIK_LAYER(vtl) );
9121 vtl->current_wp = NULL;
9122 vtl->current_wp_id = NULL;
9123 vtl->waypoint_rightclick = FALSE;
9124 vik_layer_emit_update ( VIK_LAYER(vtl) );
9128 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9130 tool_ed_t *t = data;
9131 VikViewport *vvp = t->vvp;
9133 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9138 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9141 if ( event->state & GDK_CONTROL_MASK )
9143 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9145 new_coord = tp->coord;
9149 if ( event->state & GDK_SHIFT_MASK )
9151 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9152 if ( wp && wp != vtl->current_wp )
9153 new_coord = wp->coord;
9158 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9160 marker_moveto ( t, x, y );
9167 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9169 tool_ed_t *t = data;
9170 VikViewport *vvp = t->vvp;
9172 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9175 if ( t->holding && event->button == 1 )
9178 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9181 if ( event->state & GDK_CONTROL_MASK )
9183 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9185 new_coord = tp->coord;
9189 if ( event->state & GDK_SHIFT_MASK )
9191 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9192 if ( wp && wp != vtl->current_wp )
9193 new_coord = wp->coord;
9196 marker_end_move ( t );
9198 vtl->current_wp->coord = new_coord;
9200 trw_layer_calculate_bounds_waypoints ( vtl );
9201 vik_layer_emit_update ( VIK_LAYER(vtl) );
9204 /* PUT IN RIGHT PLACE!!! */
9205 if ( event->button == 3 && vtl->waypoint_rightclick )
9207 if ( vtl->wp_right_click_menu )
9208 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9209 if ( vtl->current_wp ) {
9210 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9211 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 );
9212 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9214 vtl->waypoint_rightclick = FALSE;
9219 /*** New track ****/
9221 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9228 GdkDrawable *drawable;
9234 * Draw specified pixmap
9236 static gboolean draw_sync ( gpointer data )
9238 draw_sync_t *ds = (draw_sync_t*) data;
9239 // Sometimes don't want to draw
9240 // normally because another update has taken precedent such as panning the display
9241 // which means this pixmap is no longer valid
9242 if ( ds->vtl->draw_sync_do ) {
9243 gdk_threads_enter();
9244 gdk_draw_drawable (ds->drawable,
9247 0, 0, 0, 0, -1, -1);
9248 ds->vtl->draw_sync_done = TRUE;
9249 gdk_threads_leave();
9255 static gchar* distance_string (gdouble distance)
9259 /* draw label with distance */
9260 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9261 switch (dist_units) {
9262 case VIK_UNITS_DISTANCE_MILES:
9263 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9264 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9265 } else if (distance < 1609.4) {
9266 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9268 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9272 // VIK_UNITS_DISTANCE_KILOMETRES
9273 if (distance >= 1000 && distance < 100000) {
9274 g_sprintf(str, "%3.2f km", distance/1000.0);
9275 } else if (distance < 1000) {
9276 g_sprintf(str, "%d m", (int)distance);
9278 g_sprintf(str, "%d km", (int)distance/1000);
9282 return g_strdup (str);
9286 * Actually set the message in statusbar
9288 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9290 // Only show elevation data when track has some elevation properties
9291 gchar str_gain_loss[64];
9292 str_gain_loss[0] = '\0';
9293 gchar str_last_step[64];
9294 str_last_step[0] = '\0';
9295 gchar *str_total = distance_string (distance);
9297 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9298 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9299 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9301 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9304 if ( last_step > 0 ) {
9305 gchar *tmp = distance_string (last_step);
9306 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9310 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9312 // Write with full gain/loss information
9313 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9314 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9316 g_free ( str_total );
9320 * Figure out what information should be set in the statusbar and then write it
9322 static void update_statusbar ( VikTrwLayer *vtl )
9324 // Get elevation data
9325 gdouble elev_gain, elev_loss;
9326 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9328 /* Find out actual distance of current track */
9329 gdouble distance = vik_track_get_length (vtl->current_track);
9331 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9335 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9337 /* if we haven't sync'ed yet, we don't have time to do more. */
9338 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9339 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9341 static GdkPixmap *pixmap = NULL;
9343 // Need to check in case window has been resized
9344 w1 = vik_viewport_get_width(vvp);
9345 h1 = vik_viewport_get_height(vvp);
9347 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9349 gdk_drawable_get_size (pixmap, &w2, &h2);
9350 if (w1 != w2 || h1 != h2) {
9351 g_object_unref ( G_OBJECT ( pixmap ) );
9352 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9355 // Reset to background
9356 gdk_draw_drawable (pixmap,
9357 vtl->current_track_newpoint_gc,
9358 vik_viewport_get_pixmap(vvp),
9359 0, 0, 0, 0, -1, -1);
9361 draw_sync_t *passalong;
9364 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9366 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9367 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9368 // thus when we come to reset to the background it would include what we have already drawn!!
9369 gdk_draw_line ( pixmap,
9370 vtl->current_track_newpoint_gc,
9371 x1, y1, event->x, event->y );
9372 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9374 /* Find out actual distance of current track */
9375 gdouble distance = vik_track_get_length (vtl->current_track);
9377 // Now add distance to where the pointer is //
9380 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9381 vik_coord_to_latlon ( &coord, &ll );
9382 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9383 distance = distance + last_step;
9385 // Get elevation data
9386 gdouble elev_gain, elev_loss;
9387 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9389 // Adjust elevation data (if available) for the current pointer position
9391 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9392 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9393 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9394 // Adjust elevation of last track point
9395 if ( elev_new > last_tpt->altitude )
9397 elev_gain += elev_new - last_tpt->altitude;
9400 elev_loss += last_tpt->altitude - elev_new;
9405 // Display of the distance 'tooltip' during track creation is controlled by a preference
9407 if ( a_vik_get_create_track_tooltip() ) {
9409 gchar *str = distance_string (distance);
9411 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9412 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9413 pango_layout_set_text (pl, str, -1);
9415 pango_layout_get_pixel_size ( pl, &wd, &hd );
9418 // offset from cursor a bit depending on font size
9422 // Create a background block to make the text easier to read over the background map
9423 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9424 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9425 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9427 g_object_unref ( G_OBJECT ( pl ) );
9428 g_object_unref ( G_OBJECT ( background_block_gc ) );
9432 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9433 passalong->vtl = vtl;
9434 passalong->pixmap = pixmap;
9435 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9436 passalong->gc = vtl->current_track_newpoint_gc;
9440 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9442 // Update statusbar with full gain/loss information
9443 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9445 // draw pixmap when we have time to
9446 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9447 vtl->draw_sync_done = FALSE;
9448 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9450 return VIK_LAYER_TOOL_ACK;
9453 // NB vtl->current_track must be valid
9454 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9457 if ( vtl->current_track->trackpoints ) {
9458 // TODO rework this...
9459 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9460 GList *last = g_list_last(vtl->current_track->trackpoints);
9461 g_free ( last->data );
9462 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9464 vik_track_calculate_bounds ( vtl->current_track );
9468 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9470 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9471 vtl->current_track = NULL;
9472 vik_layer_emit_update ( VIK_LAYER(vtl) );
9474 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9475 undo_trackpoint_add ( vtl );
9476 update_statusbar ( vtl );
9477 vik_layer_emit_update ( VIK_LAYER(vtl) );
9484 * Common function to handle trackpoint button requests on either a route or a track
9485 * . enables adding a point via normal click
9486 * . enables removal of last point via right click
9487 * . finishing of the track or route via double clicking
9489 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9493 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9496 if ( event->button == 2 ) {
9497 // As the display is panning, the new track pixmap is now invalid so don't draw it
9498 // otherwise this drawing done results in flickering back to an old image
9499 vtl->draw_sync_do = FALSE;
9503 if ( event->button == 3 )
9505 if ( !vtl->current_track )
9507 undo_trackpoint_add ( vtl );
9508 update_statusbar ( vtl );
9509 vik_layer_emit_update ( VIK_LAYER(vtl) );
9513 if ( event->type == GDK_2BUTTON_PRESS )
9515 /* subtract last (duplicate from double click) tp then end */
9516 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9518 /* undo last, then end */
9519 undo_trackpoint_add ( vtl );
9520 vtl->current_track = NULL;
9522 vik_layer_emit_update ( VIK_LAYER(vtl) );
9526 tp = vik_trackpoint_new();
9527 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9529 /* snap to other TP */
9530 if ( event->state & GDK_CONTROL_MASK )
9532 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9534 tp->coord = other_tp->coord;
9537 tp->newsegment = FALSE;
9538 tp->has_timestamp = FALSE;
9541 if ( vtl->current_track ) {
9542 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9543 /* Auto attempt to get elevation from DEM data (if it's available) */
9544 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9547 vtl->ct_x1 = vtl->ct_x2;
9548 vtl->ct_y1 = vtl->ct_y2;
9549 vtl->ct_x2 = event->x;
9550 vtl->ct_y2 = event->y;
9552 vik_layer_emit_update ( VIK_LAYER(vtl) );
9556 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9558 // ----------------------------------------------------- if current is a route - switch to new track
9559 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9561 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9562 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9564 new_track_create_common ( vtl, name );
9570 return tool_new_track_or_route_click ( vtl, event, vvp );
9573 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9575 if ( event->button == 2 ) {
9576 // Pan moving ended - enable potential point drawing again
9577 vtl->draw_sync_do = TRUE;
9578 vtl->draw_sync_done = TRUE;
9582 /*** New route ****/
9584 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9589 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9591 // -------------------------- if current is a track - switch to new route
9592 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9594 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9595 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9596 new_route_create_common ( vtl, name );
9602 return tool_new_track_or_route_click ( vtl, event, vvp );
9605 /*** New waypoint ****/
9607 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9612 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9615 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9617 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9618 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9619 trw_layer_calculate_bounds_waypoints ( vtl );
9620 vik_layer_emit_update ( VIK_LAYER(vtl) );
9626 /*** Edit trackpoint ****/
9628 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9630 tool_ed_t *t = g_new(tool_ed_t, 1);
9636 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9641 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9643 tool_ed_t *t = data;
9644 VikViewport *vvp = t->vvp;
9645 TPSearchParams params;
9646 /* OUTDATED DOCUMENTATION:
9647 find 5 pixel range on each side. then put these UTM, and a pointer
9648 to the winning track name (and maybe the winning track itself), and a
9649 pointer to the winning trackpoint, inside an array or struct. pass
9650 this along, do a foreach on the tracks which will do a foreach on the
9653 params.x = event->x;
9654 params.y = event->y;
9655 params.closest_track_id = NULL;
9656 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9657 params.closest_tp = NULL;
9658 params.closest_tpl = NULL;
9659 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9661 if ( event->button != 1 )
9664 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9667 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9670 if ( vtl->current_tpl )
9672 /* 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.) */
9673 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9674 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9679 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9681 if ( current_tr->visible &&
9682 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9683 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9684 marker_begin_move ( t, event->x, event->y );
9690 if ( vtl->tracks_visible )
9691 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9693 if ( params.closest_tp )
9695 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9696 vtl->current_tpl = params.closest_tpl;
9697 vtl->current_tp_id = params.closest_track_id;
9698 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9699 trw_layer_tpwin_init ( vtl );
9700 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9701 vik_layer_emit_update ( VIK_LAYER(vtl) );
9705 if ( vtl->routes_visible )
9706 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9708 if ( params.closest_tp )
9710 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9711 vtl->current_tpl = params.closest_tpl;
9712 vtl->current_tp_id = params.closest_track_id;
9713 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9714 trw_layer_tpwin_init ( vtl );
9715 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9716 vik_layer_emit_update ( VIK_LAYER(vtl) );
9720 /* these aren't the droids you're looking for */
9724 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9726 tool_ed_t *t = data;
9727 VikViewport *vvp = t->vvp;
9729 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9735 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9738 if ( event->state & GDK_CONTROL_MASK )
9740 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9741 if ( tp && tp != vtl->current_tpl->data )
9742 new_coord = tp->coord;
9744 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9747 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9748 marker_moveto ( t, x, y );
9756 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9758 tool_ed_t *t = data;
9759 VikViewport *vvp = t->vvp;
9761 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9763 if ( event->button != 1)
9768 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9771 if ( event->state & GDK_CONTROL_MASK )
9773 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9774 if ( tp && tp != vtl->current_tpl->data )
9775 new_coord = tp->coord;
9778 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9779 if ( vtl->current_tp_track )
9780 vik_track_calculate_bounds ( vtl->current_tp_track );
9782 marker_end_move ( t );
9784 /* diff dist is diff from orig */
9786 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9788 vik_layer_emit_update ( VIK_LAYER(vtl) );
9795 /*** Route Finder ***/
9796 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9801 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9804 if ( !vtl ) return FALSE;
9805 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9806 if ( event->button == 3 && vtl->route_finder_current_track ) {
9808 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9810 vtl->route_finder_coord = *new_end;
9812 vik_layer_emit_update ( VIK_LAYER(vtl) );
9813 /* remove last ' to:...' */
9814 if ( vtl->route_finder_current_track->comment ) {
9815 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9816 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9817 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9818 last_to - vtl->route_finder_current_track->comment - 1);
9819 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9824 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9825 struct LatLon start, end;
9827 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9828 vik_coord_to_latlon ( &(tmp), &end );
9829 vtl->route_finder_coord = tmp; /* for continuations */
9831 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9832 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9833 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9835 vtl->route_finder_check_added_track = TRUE;
9836 vtl->route_finder_started = FALSE;
9839 vik_routing_default_find ( vtl, start, end);
9841 /* see if anything was done -- a track was added or appended to */
9842 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9843 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 ) );
9844 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9845 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9846 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9847 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9850 if ( vtl->route_finder_added_track )
9851 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9853 vtl->route_finder_added_track = NULL;
9854 vtl->route_finder_check_added_track = FALSE;
9855 vtl->route_finder_append = FALSE;
9857 vik_layer_emit_update ( VIK_LAYER(vtl) );
9859 vtl->route_finder_started = TRUE;
9860 vtl->route_finder_coord = tmp;
9861 vtl->route_finder_current_track = NULL;
9866 /*** Show picture ****/
9868 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9873 /* Params are: vvp, event, last match found or NULL */
9874 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9876 if ( wp->image && wp->visible )
9878 gint x, y, slackx, slacky;
9879 GdkEventButton *event = (GdkEventButton *) params[1];
9881 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9882 slackx = wp->image_width / 2;
9883 slacky = wp->image_height / 2;
9884 if ( x <= event->x + slackx && x >= event->x - slackx
9885 && y <= event->y + slacky && y >= event->y - slacky )
9887 params[2] = wp->image; /* we've found a match. however continue searching
9888 * since we want to find the last match -- that
9889 * is, the match that was drawn last. */
9894 static void trw_layer_show_picture ( menu_array_sublayer values )
9896 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9898 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9901 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9902 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9903 g_free ( quoted_file );
9904 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9906 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() );
9907 g_error_free ( err );
9910 #endif /* WINDOWS */
9913 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9915 gpointer params[3] = { vvp, event, NULL };
9916 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9918 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9921 static menu_array_sublayer values;
9922 values[MA_VTL] = vtl;
9923 values[MA_MISC] = params[2];
9924 trw_layer_show_picture ( values );
9925 return TRUE; /* found a match */
9928 return FALSE; /* go through other layers, searching for a match */
9931 /***************************************************************************
9933 ***************************************************************************/
9936 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9938 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9939 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9942 /* Structure for thumbnail creating data used in the background thread */
9944 VikTrwLayer *vtl; // Layer needed for redrawing
9945 GSList *pics; // Image list
9946 } thumbnail_create_thread_data;
9948 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9950 guint total = g_slist_length(tctd->pics), done = 0;
9951 while ( tctd->pics )
9953 a_thumbnails_create ( (gchar *) tctd->pics->data );
9954 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9956 return -1; /* Abort thread */
9958 tctd->pics = tctd->pics->next;
9961 // Redraw to show the thumbnails as they are now created
9962 if ( IS_VIK_LAYER(tctd->vtl) )
9963 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9968 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9970 while ( tctd->pics )
9972 g_free ( tctd->pics->data );
9973 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9978 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9980 if ( ! vtl->has_verified_thumbnails )
9982 GSList *pics = NULL;
9983 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9986 gint len = g_slist_length ( pics );
9987 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9988 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9991 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9993 (vik_thr_func) create_thumbnails_thread,
9995 (vik_thr_free_func) thumbnail_create_thread_free,
10003 static const gchar* my_track_colors ( gint ii )
10005 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10017 // Fast and reliable way of returning a colour
10018 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10021 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10023 GHashTableIter iter;
10024 gpointer key, value;
10028 g_hash_table_iter_init ( &iter, vtl->tracks );
10030 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10032 // Tracks get a random spread of colours if not already assigned
10033 if ( ! VIK_TRACK(value)->has_color ) {
10034 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10035 VIK_TRACK(value)->color = vtl->track_color;
10037 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10039 VIK_TRACK(value)->has_color = TRUE;
10042 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10045 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10051 g_hash_table_iter_init ( &iter, vtl->routes );
10053 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10055 // Routes get an intermix of reds
10056 if ( ! VIK_TRACK(value)->has_color ) {
10058 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10060 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10061 VIK_TRACK(value)->has_color = TRUE;
10064 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10071 * (Re)Calculate the bounds of the waypoints in this layer,
10072 * This should be called whenever waypoints are changed
10074 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10076 struct LatLon topleft = { 0.0, 0.0 };
10077 struct LatLon bottomright = { 0.0, 0.0 };
10080 GHashTableIter iter;
10081 gpointer key, value;
10083 g_hash_table_iter_init ( &iter, vtl->waypoints );
10085 // Set bounds to first point
10086 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10087 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10088 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10091 // Ensure there is another point...
10092 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10094 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10096 // See if this point increases the bounds.
10097 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10099 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10100 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10101 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10102 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10106 vtl->waypoints_bbox.north = topleft.lat;
10107 vtl->waypoints_bbox.east = bottomright.lon;
10108 vtl->waypoints_bbox.south = bottomright.lat;
10109 vtl->waypoints_bbox.west = topleft.lon;
10112 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10114 vik_track_calculate_bounds ( trk );
10117 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10119 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10120 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10123 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10125 if ( ! VIK_LAYER(vtl)->vt )
10128 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10129 if ( g_hash_table_size (vtl->tracks) > 1 )
10130 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10132 if ( g_hash_table_size (vtl->routes) > 1 )
10133 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10135 if ( g_hash_table_size (vtl->waypoints) > 1 )
10136 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10139 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10141 if ( VIK_LAYER(vtl)->realized )
10142 trw_layer_verify_thumbnails ( vtl, vvp );
10143 trw_layer_track_alloc_colors ( vtl );
10145 trw_layer_calculate_bounds_waypoints ( vtl );
10146 trw_layer_calculate_bounds_tracks ( vtl );
10148 // Apply treeview sort after loading all the tracks for this layer
10149 // (rather than sorted insert on each individual track additional)
10150 // and after subsequent changes to the properties as the specified order may have changed.
10151 // since the sorting of a treeview section is now very quick
10152 // NB sorting is also performed after every name change as well to maintain the list order
10153 trw_layer_sort_all ( vtl );
10155 // Setting metadata time if not otherwise set
10156 if ( vtl->metadata ) {
10158 gboolean need_to_set_time = TRUE;
10159 if ( vtl->metadata->timestamp ) {
10160 need_to_set_time = FALSE;
10161 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10162 need_to_set_time = TRUE;
10165 if ( need_to_set_time ) {
10166 // Could rewrite this as a general get first time of a TRW Layer function
10167 GTimeVal timestamp;
10168 timestamp.tv_usec = 0;
10169 gboolean has_timestamp = FALSE;
10172 gl = g_hash_table_get_values ( vtl->tracks );
10173 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10174 gl = g_list_first ( gl );
10176 // Check times of tracks
10178 // Only need to check the first track as they have been sorted by time
10179 VikTrack *trk = (VikTrack*)gl->data;
10180 // Assume trackpoints already sorted by time
10181 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10182 if ( tpt && tpt->has_timestamp ) {
10183 timestamp.tv_sec = tpt->timestamp;
10184 has_timestamp = TRUE;
10186 g_list_free ( gl );
10189 if ( !has_timestamp ) {
10190 // 'Last' resort - current time
10191 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10192 g_get_current_time ( ×tamp );
10194 // Check times of waypoints
10195 gl = g_hash_table_get_values ( vtl->waypoints );
10197 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10198 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10199 if ( wpt->has_timestamp ) {
10200 if ( timestamp.tv_sec > wpt->timestamp ) {
10201 timestamp.tv_sec = wpt->timestamp;
10202 has_timestamp = TRUE;
10206 g_list_free ( gl );
10209 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10214 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10216 return vtl->coord_mode;
10220 * Uniquify the whole layer
10221 * Also requires the layers panel as the names shown there need updating too
10222 * Returns whether the operation was successful or not
10224 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10226 if ( vtl && vlp ) {
10227 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10228 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10229 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10235 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10237 vik_coord_convert ( &(wp->coord), *dest_mode );
10240 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10242 vik_track_convert ( tr, *dest_mode );
10245 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10247 if ( vtl->coord_mode != dest_mode )
10249 vtl->coord_mode = dest_mode;
10250 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10251 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10252 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10256 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10258 vtl->menu_selection = selection;
10261 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10263 return (vtl->menu_selection);
10266 /* ----------- Downloading maps along tracks --------------- */
10268 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10270 /* TODO: calculating based on current size of viewport */
10271 const gdouble w_at_zoom_0_125 = 0.0013;
10272 const gdouble h_at_zoom_0_125 = 0.0011;
10273 gdouble zoom_factor = zoom_level/0.125;
10275 wh->lat = h_at_zoom_0_125 * zoom_factor;
10276 wh->lon = w_at_zoom_0_125 * zoom_factor;
10278 return 0; /* all OK */
10281 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10283 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10284 (dist->lat >= ABS(to->north_south - from->north_south)))
10287 VikCoord *coord = g_malloc(sizeof(VikCoord));
10288 coord->mode = VIK_COORD_LATLON;
10290 if (ABS(gradient) < 1) {
10291 if (from->east_west > to->east_west)
10292 coord->east_west = from->east_west - dist->lon;
10294 coord->east_west = from->east_west + dist->lon;
10295 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10297 if (from->north_south > to->north_south)
10298 coord->north_south = from->north_south - dist->lat;
10300 coord->north_south = from->north_south + dist->lat;
10301 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10307 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10309 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10310 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10312 VikCoord *next = from;
10314 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10316 list = g_list_prepend(list, next);
10322 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10324 typedef struct _Rect {
10329 #define GLRECT(iter) ((Rect *)((iter)->data))
10332 GList *rects_to_download = NULL;
10335 if (get_download_area_width(vvp, zoom_level, &wh))
10338 GList *iter = tr->trackpoints;
10342 gboolean new_map = TRUE;
10343 VikCoord *cur_coord, tl, br;
10346 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10348 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10349 rect = g_malloc(sizeof(Rect));
10352 rect->center = *cur_coord;
10353 rects_to_download = g_list_prepend(rects_to_download, rect);
10358 gboolean found = FALSE;
10359 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10360 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10371 GList *fillins = NULL;
10372 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10373 /* seems that ATM the function get_next_coord works only for LATLON */
10374 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10375 /* fill-ins for far apart points */
10376 GList *cur_rect, *next_rect;
10377 for (cur_rect = rects_to_download;
10378 (next_rect = cur_rect->next) != NULL;
10379 cur_rect = cur_rect->next) {
10380 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10381 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10382 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10386 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10389 GList *iter = fillins;
10391 cur_coord = (VikCoord *)(iter->data);
10392 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10393 rect = g_malloc(sizeof(Rect));
10396 rect->center = *cur_coord;
10397 rects_to_download = g_list_prepend(rects_to_download, rect);
10402 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10403 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10407 for (iter = fillins; iter; iter = iter->next)
10408 g_free(iter->data);
10409 g_list_free(fillins);
10411 if (rects_to_download) {
10412 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10413 g_free(rect_iter->data);
10414 g_list_free(rects_to_download);
10418 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10422 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10423 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10424 gint selected_zoom, default_zoom;
10426 VikTrwLayer *vtl = values[MA_VTL];
10427 VikLayersPanel *vlp = values[MA_VLP];
10429 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10430 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10432 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10436 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10438 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10439 int num_maps = g_list_length(vmls);
10442 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10446 // Convert from list of vmls to list of names. Allowing the user to select one of them
10447 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10448 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10450 gchar **np = map_names;
10451 VikMapsLayer **lp = map_layers;
10453 for (i = 0; i < num_maps; i++) {
10454 vml = (VikMapsLayer *)(vmls->data);
10456 *np++ = vik_maps_layer_get_map_label(vml);
10459 // Mark end of the array lists
10463 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10464 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10465 if (cur_zoom == zoom_vals[default_zoom])
10468 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10470 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10473 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10476 for (i = 0; i < num_maps; i++)
10477 g_free(map_names[i]);
10479 g_free(map_layers);
10485 /**** lowest waypoint number calculation ***/
10486 static gint highest_wp_number_name_to_number(const gchar *name) {
10487 if ( strlen(name) == 3 ) {
10488 int n = atoi(name);
10489 if ( n < 100 && name[0] != '0' )
10491 if ( n < 10 && name[0] != '0' )
10499 static void highest_wp_number_reset(VikTrwLayer *vtl)
10501 vtl->highest_wp_number = -1;
10504 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10506 /* if is bigger that top, add it */
10507 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10508 if ( new_wp_num > vtl->highest_wp_number )
10509 vtl->highest_wp_number = new_wp_num;
10512 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10514 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10515 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10516 if ( vtl->highest_wp_number == old_wp_num ) {
10518 vtl->highest_wp_number--;
10520 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10521 /* search down until we find something that *does* exist */
10523 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10524 vtl->highest_wp_number--;
10525 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10530 /* get lowest unused number */
10531 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10534 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10536 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10537 return g_strdup(buf);
10541 * trw_layer_create_track_list_both:
10543 * Create the latest list of tracks and routes
10545 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10547 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10548 GList *tracks = NULL;
10549 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10550 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10552 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10555 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10557 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10559 gchar *title = NULL;
10560 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10561 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10563 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10565 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10569 static void trw_layer_track_list_dialog ( menu_array_layer values )
10571 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10573 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10574 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10578 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10580 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10582 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10583 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );