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, NULL },
423 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
424 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
425 (VikToolMouseFunc) tool_new_track_click,
426 (VikToolMouseMoveFunc) tool_new_track_move,
427 (VikToolMouseFunc) tool_new_track_release,
428 (VikToolKeyFunc) tool_new_track_key_press,
429 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
430 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
432 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
433 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
434 (VikToolMouseFunc) tool_new_route_click,
435 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
436 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
437 (VikToolKeyFunc) tool_new_track_key_press, // -/#
438 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
439 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
441 { { "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, NULL },
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, NULL },
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, NULL },
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, NULL },
475 TOOL_CREATE_WAYPOINT=0,
479 TOOL_EDIT_TRACKPOINT,
485 /****** PARAMETERS ******/
487 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
488 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
490 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
491 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
493 #define MIN_POINT_SIZE 2
494 #define MAX_POINT_SIZE 10
496 #define MIN_ARROW_SIZE 3
497 #define MAX_ARROW_SIZE 20
499 static VikLayerParamScale params_scales[] = {
500 /* min max step digits */
501 { 1, 10, 1, 0 }, /* line_thickness */
502 { 0, 100, 1, 0 }, /* track draw speed factor */
503 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
504 /* 5 * step == how much to turn */
505 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
506 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
507 { 5, 500, 5, 0 }, // 5: image cache_size - " "
508 { 0, 8, 1, 0 }, // 6: Background line thickness
509 { 1, 64, 1, 0 }, /* wpsize */
510 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
511 { 1, 100, 1, 0 }, // 9: elevation factor
512 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
513 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
516 static gchar* params_font_sizes[] = {
517 N_("Extra Extra Small"),
523 N_("Extra Extra Large"),
526 // Needs to align with vik_layer_sort_order_t
527 static gchar* params_sort_order[] = {
529 N_("Name Ascending"),
530 N_("Name Descending"),
534 static VikLayerParamData black_color_default ( void ) {
535 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
537 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
538 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
539 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
540 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
541 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
542 static VikLayerParamData trackbgcolor_default ( void ) {
543 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
545 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
546 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
547 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
549 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
550 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
551 static VikLayerParamData wptextcolor_default ( void ) {
552 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
554 static VikLayerParamData wpbgcolor_default ( void ) {
555 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
557 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
558 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
560 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
561 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
562 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
564 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
566 static VikLayerParamData string_default ( void )
568 VikLayerParamData data;
573 VikLayerParam trw_layer_params[] = {
574 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
575 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
576 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
578 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
579 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
580 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
581 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
582 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
583 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
584 { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
585 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
586 { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
587 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
588 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
589 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
590 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
591 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
592 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
593 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
594 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
596 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
597 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
598 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
599 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
600 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
602 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
603 { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
604 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
605 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
606 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
607 { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
608 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
609 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
610 { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
611 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
613 { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
614 { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[3], NULL, NULL, image_size_default, NULL, NULL },
615 { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
616 { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
618 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
619 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
620 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
621 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
626 // Sublayer visibilities
674 *** 1) Add to trw_layer_params and enumeration
675 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
678 /****** END PARAMETERS ******/
680 /* Layer Interface function definitions */
681 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
682 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
683 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
684 static void trw_layer_free ( VikTrwLayer *trwlayer );
685 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
686 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
687 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
688 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
689 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
690 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
691 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
692 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
693 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
694 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
695 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
696 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
697 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
698 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
699 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
700 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
701 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
702 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
703 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
704 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
705 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
706 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
707 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
708 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
709 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
710 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
711 /* End Layer Interface function definitions */
713 VikLayerInterface vik_trw_layer_interface = {
720 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
724 params_groups, /* params_groups */
725 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
729 (VikLayerFuncCreate) trw_layer_create,
730 (VikLayerFuncRealize) trw_layer_realize,
731 (VikLayerFuncPostRead) trw_layer_post_read,
732 (VikLayerFuncFree) trw_layer_free,
734 (VikLayerFuncProperties) NULL,
735 (VikLayerFuncDraw) trw_layer_draw,
736 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
738 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
739 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
741 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
742 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
744 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
745 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
746 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
747 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
748 (VikLayerFuncLayerSelected) trw_layer_selected,
750 (VikLayerFuncMarshall) trw_layer_marshall,
751 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
753 (VikLayerFuncSetParam) trw_layer_set_param,
754 (VikLayerFuncGetParam) trw_layer_get_param,
755 (VikLayerFuncChangeParam) trw_layer_change_param,
757 (VikLayerFuncReadFileData) a_gpspoint_read_file,
758 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
760 (VikLayerFuncDeleteItem) trw_layer_del_item,
761 (VikLayerFuncCutItem) trw_layer_cut_item,
762 (VikLayerFuncCopyItem) trw_layer_copy_item,
763 (VikLayerFuncPasteItem) trw_layer_paste_item,
764 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
766 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
768 (VikLayerFuncSelectClick) trw_layer_select_click,
769 (VikLayerFuncSelectMove) trw_layer_select_move,
770 (VikLayerFuncSelectRelease) trw_layer_select_release,
771 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
774 static gboolean have_diary_program = FALSE;
776 // NB Only performed once per program run
777 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
779 if ( g_find_program_in_path( "rednotebook" ) ) {
780 gchar *stdout = NULL;
781 gchar *stderr = NULL;
782 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
783 if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
784 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
786 g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
788 g_warning ("Diary: stderr: %s", stderr );
790 gchar **tokens = NULL;
791 if ( stdout && g_strcmp0(stdout, "") )
792 tokens = g_strsplit(stdout, " ", 0);
794 tokens = g_strsplit(stderr, " ", 0);
797 gchar *token = tokens[num];
798 while ( token && num < 2 ) {
800 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
801 have_diary_program = TRUE;
806 g_strfreev ( tokens );
813 GType vik_trw_layer_get_type ()
815 static GType vtl_type = 0;
819 static const GTypeInfo vtl_info =
821 sizeof (VikTrwLayerClass),
822 NULL, /* base_init */
823 NULL, /* base_finalize */
824 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
825 NULL, /* class_finalize */
826 NULL, /* class_data */
827 sizeof (VikTrwLayer),
829 NULL /* instance init */
831 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
836 VikTRWMetadata *vik_trw_metadata_new()
838 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
841 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
846 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
848 return vtl->metadata;
851 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
854 vik_trw_metadata_free ( vtl->metadata );
855 vtl->metadata = metadata;
860 const gchar *date_str;
862 const VikWaypoint *wpt;
867 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
871 // Might be an easier way to compare dates rather than converting the strings all the time...
872 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
873 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
875 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
884 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
888 // Might be an easier way to compare dates rather than converting the strings all the time...
889 if ( wpt->has_timestamp ) {
890 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
892 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
902 * Find an item by date
904 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
908 df.date_str = date_str;
913 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
915 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
917 if ( select && df.found ) {
918 if ( do_tracks && df.trk ) {
919 struct LatLon maxmin[2] = { {0,0}, {0,0} };
920 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
921 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
922 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
925 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
926 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
928 vik_layer_emit_update ( VIK_LAYER(vtl) );
933 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
935 static menu_array_sublayer values;
941 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
944 values[MA_VTL] = vtl;
945 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
946 values[MA_SUBLAYER_ID] = sublayer;
947 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
949 trw_layer_delete_item ( values );
952 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
954 static menu_array_sublayer values;
960 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
963 values[MA_VTL] = vtl;
964 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
965 values[MA_SUBLAYER_ID] = sublayer;
966 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
968 trw_layer_copy_item_cb(values);
969 trw_layer_cut_item_cb(values);
972 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
974 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
975 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
976 gpointer * sublayer = values[MA_SUBLAYER_ID];
980 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
984 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
985 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
986 if ( wp && wp->name )
989 name = NULL; // Broken :(
991 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
992 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
993 if ( trk && trk->name )
996 name = NULL; // Broken :(
999 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1000 if ( trk && trk->name )
1003 name = NULL; // Broken :(
1006 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1007 subtype, len, name, data);
1011 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1013 trw_layer_copy_item_cb(values);
1014 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1015 trw_layer_delete_item(values);
1018 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1020 // Slightly cheating method, routing via the panels capability
1021 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1024 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1034 GByteArray *ba = g_byte_array_new ();
1036 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1037 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1038 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1039 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1041 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1044 g_byte_array_append ( ba, id, il );
1052 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1059 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1063 w = vik_waypoint_unmarshall ( item, len );
1064 // When copying - we'll create a new name based on the original
1065 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1066 vik_trw_layer_add_waypoint ( vtl, name, w );
1067 waypoint_convert (NULL, w, &vtl->coord_mode);
1070 trw_layer_calculate_bounds_waypoints ( vtl );
1072 // Consider if redraw necessary for the new item
1073 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1074 vik_layer_emit_update ( VIK_LAYER(vtl) );
1077 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1081 t = vik_track_unmarshall ( item, len );
1082 // When copying - we'll create a new name based on the original
1083 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1084 vik_trw_layer_add_track ( vtl, name, t );
1085 vik_track_convert (t, vtl->coord_mode);
1088 // Consider if redraw necessary for the new item
1089 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1090 vik_layer_emit_update ( VIK_LAYER(vtl) );
1093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1097 t = vik_track_unmarshall ( item, len );
1098 // When copying - we'll create a new name based on the original
1099 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1100 vik_trw_layer_add_route ( vtl, name, t );
1101 vik_track_convert (t, vtl->coord_mode);
1104 // Consider if redraw necessary for the new item
1105 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1106 vik_layer_emit_update ( VIK_LAYER(vtl) );
1112 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1119 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1123 case PARAM_TV: vtl->tracks_visible = data.b; break;
1124 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1125 case PARAM_RV: vtl->routes_visible = data.b; break;
1126 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1127 case PARAM_TLFONTSIZE:
1128 if ( data.u < FS_NUM_SIZES ) {
1129 vtl->track_font_size = data.u;
1130 g_free ( vtl->track_fsize_str );
1131 switch ( vtl->track_font_size ) {
1132 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1133 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1134 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1135 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1136 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1137 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1138 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1142 case PARAM_DM: vtl->drawmode = data.u; break;
1144 vtl->track_color = data.c;
1145 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1147 case PARAM_DP: vtl->drawpoints = data.b; break;
1149 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1150 vtl->drawpoints_size = data.u;
1152 case PARAM_DE: vtl->drawelevation = data.b; break;
1153 case PARAM_DS: vtl->drawstops = data.b; break;
1154 case PARAM_DL: vtl->drawlines = data.b; break;
1155 case PARAM_DD: vtl->drawdirections = data.b; break;
1157 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1158 vtl->drawdirections_size = data.u;
1160 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1161 vtl->stop_length = data.u;
1163 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1164 vtl->elevation_factor = data.u;
1166 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1168 vtl->line_thickness = data.u;
1169 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1172 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1174 vtl->bg_line_thickness = data.u;
1175 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1179 vtl->track_bg_color = data.c;
1180 if ( vtl->track_bg_gc )
1181 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1183 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1184 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1185 case PARAM_DLA: vtl->drawlabels = data.b; break;
1186 case PARAM_DI: vtl->drawimages = data.b; break;
1187 case PARAM_IS: if ( data.u != vtl->image_size )
1189 vtl->image_size = data.u;
1190 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1191 g_queue_free ( vtl->image_cache );
1192 vtl->image_cache = g_queue_new ();
1195 case PARAM_IA: vtl->image_alpha = data.u; break;
1196 case PARAM_ICS: vtl->image_cache_size = data.u;
1197 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1198 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1201 vtl->waypoint_color = data.c;
1202 if ( vtl->waypoint_gc )
1203 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1206 vtl->waypoint_text_color = data.c;
1207 if ( vtl->waypoint_text_gc )
1208 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1211 vtl->waypoint_bg_color = data.c;
1212 if ( vtl->waypoint_bg_gc )
1213 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1216 vtl->wpbgand = data.b;
1217 if ( vtl->waypoint_bg_gc )
1218 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1220 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1221 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1222 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1223 case PARAM_WPFONTSIZE:
1224 if ( data.u < FS_NUM_SIZES ) {
1225 vtl->wp_font_size = data.u;
1226 g_free ( vtl->wp_fsize_str );
1227 switch ( vtl->wp_font_size ) {
1228 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1229 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1230 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1231 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1232 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1233 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1234 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1238 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1240 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1241 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1242 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1243 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1249 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1251 VikLayerParamData rv;
1254 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1255 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1256 case PARAM_RV: rv.b = vtl->routes_visible; break;
1257 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1258 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1259 case PARAM_DM: rv.u = vtl->drawmode; break;
1260 case PARAM_TC: rv.c = vtl->track_color; break;
1261 case PARAM_DP: rv.b = vtl->drawpoints; break;
1262 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1263 case PARAM_DE: rv.b = vtl->drawelevation; break;
1264 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1265 case PARAM_DS: rv.b = vtl->drawstops; break;
1266 case PARAM_SL: rv.u = vtl->stop_length; break;
1267 case PARAM_DL: rv.b = vtl->drawlines; break;
1268 case PARAM_DD: rv.b = vtl->drawdirections; break;
1269 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1270 case PARAM_LT: rv.u = vtl->line_thickness; break;
1271 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1272 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1273 case PARAM_DI: rv.b = vtl->drawimages; break;
1274 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1275 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1276 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1277 case PARAM_IS: rv.u = vtl->image_size; break;
1278 case PARAM_IA: rv.u = vtl->image_alpha; break;
1279 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1280 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1281 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1282 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1283 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1284 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1285 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1286 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1287 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1288 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1290 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1291 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1292 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1293 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1299 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1301 // This '-3' is to account for the first few parameters not in the properties
1302 const gint OFFSET = -3;
1304 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1305 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1308 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1309 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1310 GtkWidget **ww2 = values[UI_CHG_LABELS];
1311 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1312 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1313 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1314 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1315 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1316 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1317 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1318 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1319 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1320 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1321 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1322 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1325 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1328 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1329 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1330 GtkWidget **ww2 = values[UI_CHG_LABELS];
1331 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1332 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1333 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1334 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1335 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1336 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1337 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1338 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1339 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1340 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1341 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1342 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1343 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1344 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1345 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1346 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1349 // Alter sensitivity of all track colours according to the draw track mode.
1352 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1353 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1354 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1355 GtkWidget **ww2 = values[UI_CHG_LABELS];
1356 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1357 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1358 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1359 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1362 case PARAM_MDTIME: {
1363 // Force metadata->timestamp to be always read-only for now.
1364 GtkWidget **ww = values[UI_CHG_WIDGETS];
1365 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1366 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1368 // NB Since other track settings have been split across tabs,
1369 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1374 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1381 // Use byte arrays to store sublayer data
1382 // much like done elsewhere e.g. vik_layer_marshall_params()
1383 GByteArray *ba = g_byte_array_new ( );
1388 guint object_length;
1391 // the length of the item
1392 // the sublayer type of item
1393 // the the actual item
1394 #define tlm_append(object_pointer, size, type) \
1396 object_length = (size); \
1397 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1398 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1399 g_byte_array_append ( ba, (object_pointer), object_length );
1401 // Layer parameters first
1402 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1403 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1404 g_byte_array_append ( ba, pd, pl );
1407 // Now sublayer data
1408 GHashTableIter iter;
1409 gpointer key, value;
1412 g_hash_table_iter_init ( &iter, vtl->waypoints );
1413 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1414 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1415 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1420 g_hash_table_iter_init ( &iter, vtl->tracks );
1421 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1422 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1423 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1428 g_hash_table_iter_init ( &iter, vtl->routes );
1429 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1430 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1431 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1441 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1443 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1445 gint consumed_length;
1447 // First the overall layer parameters
1448 memcpy(&pl, data, sizeof(pl));
1450 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1453 consumed_length = pl;
1454 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1456 #define tlm_size (*(gint *)data)
1457 // See marshalling above for order of how this is written
1459 data += sizeof_len_and_subtype + tlm_size;
1461 // Now the individual sublayers:
1463 while ( *data && consumed_length < len ) {
1464 // Normally four extra bytes at the end of the datastream
1465 // (since it's a GByteArray and that's where it's length is stored)
1466 // So only attempt read when there's an actual block of sublayer data
1467 if ( consumed_length + tlm_size < len ) {
1469 // Reuse pl to read the subtype from the data stream
1470 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1472 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1473 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1474 gchar *name = g_strdup ( trk->name );
1475 vik_trw_layer_add_track ( vtl, name, trk );
1478 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1479 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1480 gchar *name = g_strdup ( wp->name );
1481 vik_trw_layer_add_waypoint ( vtl, name, wp );
1484 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1485 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1486 gchar *name = g_strdup ( trk->name );
1487 vik_trw_layer_add_route ( vtl, name, trk );
1491 consumed_length += tlm_size + sizeof_len_and_subtype;
1494 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1496 // Not stored anywhere else so need to regenerate
1497 trw_layer_calculate_bounds_waypoints ( vtl );
1502 // Keep interesting hash function at least visible
1504 static guint strcase_hash(gconstpointer v)
1506 // 31 bit hash function
1509 gchar s[128]; // malloc is too slow for reading big files
1512 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1513 p[i] = toupper(t[i]);
1519 for (p += 1; *p != '\0'; p++)
1520 h = (h << 5) - h + *p;
1527 // Stick a 1 at the end of the function name to make it more unique
1528 // thus more easily searchable in a simple text editor
1529 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1531 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1532 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1534 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1535 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1537 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1538 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1539 // and with normal PC processing capabilities - it has negligibile performance impact
1540 // This also minimized the amount of rework - as the management of the hash tables already exists.
1542 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1543 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1544 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1546 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1547 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1548 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1549 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1550 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1551 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1553 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1555 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1557 // Param settings that are not available via the GUI
1558 // Force to on after processing params (which defaults them to off with a zero value)
1559 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1561 rv->metadata = vik_trw_metadata_new ();
1562 rv->draw_sync_done = TRUE;
1563 rv->draw_sync_do = TRUE;
1564 // Everything else is 0, FALSE or NULL
1570 static void trw_layer_free ( VikTrwLayer *trwlayer )
1572 g_hash_table_destroy(trwlayer->waypoints);
1573 g_hash_table_destroy(trwlayer->waypoints_iters);
1574 g_hash_table_destroy(trwlayer->tracks);
1575 g_hash_table_destroy(trwlayer->tracks_iters);
1576 g_hash_table_destroy(trwlayer->routes);
1577 g_hash_table_destroy(trwlayer->routes_iters);
1579 /* ODC: replace with GArray */
1580 trw_layer_free_track_gcs ( trwlayer );
1582 if ( trwlayer->wp_right_click_menu )
1583 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1585 if ( trwlayer->track_right_click_menu )
1586 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1588 if ( trwlayer->tracklabellayout != NULL)
1589 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1591 if ( trwlayer->wplabellayout != NULL)
1592 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1594 if ( trwlayer->waypoint_gc != NULL )
1595 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1597 if ( trwlayer->waypoint_text_gc != NULL )
1598 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1600 if ( trwlayer->waypoint_bg_gc != NULL )
1601 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1603 g_free ( trwlayer->wp_fsize_str );
1604 g_free ( trwlayer->track_fsize_str );
1606 if ( trwlayer->tpwin != NULL )
1607 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1609 if ( trwlayer->tracks_analysis_dialog != NULL )
1610 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1612 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1613 g_queue_free ( trwlayer->image_cache );
1616 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1620 dp->highlight = highlight;
1621 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1622 dp->xmpp = vik_viewport_get_xmpp ( vp );
1623 dp->ympp = vik_viewport_get_ympp ( vp );
1624 dp->width = vik_viewport_get_width ( vp );
1625 dp->height = vik_viewport_get_height ( vp );
1626 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1627 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1629 dp->center = vik_viewport_get_center ( vp );
1630 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1631 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1636 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1637 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1638 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1640 dp->ce1 = dp->center->east_west-w2;
1641 dp->ce2 = dp->center->east_west+w2;
1642 dp->cn1 = dp->center->north_south-h2;
1643 dp->cn2 = dp->center->north_south+h2;
1644 } else if ( dp->lat_lon ) {
1645 VikCoord upperleft, bottomright;
1646 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1647 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1648 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1649 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1650 dp->ce1 = upperleft.east_west;
1651 dp->ce2 = bottomright.east_west;
1652 dp->cn1 = bottomright.north_south;
1653 dp->cn2 = upperleft.north_south;
1656 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1660 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1661 * Here a simple traffic like light colour system is used:
1662 * . slow points are red
1663 * . average is yellow
1664 * . fast points are green
1666 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1669 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1670 if ( average_speed > 0 ) {
1671 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1672 if ( rv < low_speed )
1673 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1674 else if ( rv > high_speed )
1675 return VIK_TRW_LAYER_TRACK_GC_FAST;
1677 return VIK_TRW_LAYER_TRACK_GC_AVER;
1680 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1683 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1685 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1686 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1687 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1688 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1692 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1694 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1696 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1697 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1699 // Fallback if parse failure
1700 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1702 g_free ( label_markup );
1704 gint label_x, label_y;
1706 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1708 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1709 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1713 * distance_in_preferred_units:
1714 * @dist: The source distance in standard SI Units (i.e. metres)
1716 * TODO: This is a generic function that could be moved into globals.c or utils.c
1718 * Probably best used if you have a only few conversions to perform.
1719 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1720 * since it will be doing the preference check on each call
1722 * Returns: The distance in the units as specified by the preferences
1724 static gdouble distance_in_preferred_units ( gdouble dist )
1727 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1728 switch (dist_units) {
1729 case VIK_UNITS_DISTANCE_MILES:
1730 mydist = VIK_METERS_TO_MILES(dist);
1732 // VIK_UNITS_DISTANCE_KILOMETRES:
1734 mydist = dist/1000.0;
1741 * trw_layer_draw_dist_labels:
1743 * Draw a few labels along a track at nicely seperated distances
1744 * This might slow things down if there's many tracks being displayed with this on.
1746 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1748 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1749 25.0, 40.0, 50.0, 75.0, 100.0,
1750 150.0, 200.0, 250.0, 500.0, 1000.0};
1752 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1754 // Convert to specified unit to find the friendly breakdown value
1755 dist = distance_in_preferred_units ( dist );
1759 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1760 if ( chunksd[i] > dist ) {
1762 dist = chunksd[index];
1767 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1769 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1770 gdouble dist_i = dist * i;
1772 // Convert distance back into metres for use in finding a trackpoint
1773 switch (dist_units) {
1774 case VIK_UNITS_DISTANCE_MILES:
1775 dist_i = VIK_MILES_TO_METERS(dist_i);
1777 // VIK_UNITS_DISTANCE_KILOMETRES:
1779 dist_i = dist_i*1000.0;
1783 gdouble dist_current = 0.0;
1784 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1785 gdouble dist_next = 0.0;
1786 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1788 gdouble dist_between_tps = fabs (dist_next - dist_current);
1789 gdouble ratio = 0.0;
1790 // Prevent division by 0 errors
1791 if ( dist_between_tps > 0.0 )
1792 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1794 if ( tp_current && tp_next ) {
1795 // Construct the name based on the distance value
1798 switch (dist_units) {
1799 case VIK_UNITS_DISTANCE_MILES:
1800 units = g_strdup ( _("miles") );
1802 // VIK_UNITS_DISTANCE_KILOMETRES:
1804 units = g_strdup ( _("km") );
1808 // Convert for display
1809 dist_i = distance_in_preferred_units ( dist_i );
1811 // Make the precision of the output related to the unit size.
1813 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1814 else if ( index == 1 )
1815 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1817 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1820 struct LatLon ll_current, ll_next;
1821 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1822 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1824 // positional interpolation
1825 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1826 // but should be good enough over the small scale that I anticipate usage on
1827 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1828 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1830 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1833 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1834 fgcolour = gdk_color_to_string ( &(trk->color) );
1836 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1838 // if highlight mode on, then colour the background in the highlight colour
1840 if ( drawing_highlight )
1841 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1843 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1845 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1847 g_free ( fgcolour );
1848 g_free ( bgcolour );
1855 * trw_layer_draw_track_name_labels:
1857 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1859 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1862 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1863 fgcolour = gdk_color_to_string ( &(trk->color) );
1865 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1867 // if highlight mode on, then colour the background in the highlight colour
1869 if ( drawing_highlight )
1870 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1872 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1874 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1876 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1877 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1878 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1879 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1880 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1881 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1883 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1885 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1888 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1889 // No other labels to draw
1892 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1895 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1898 VikCoord begin_coord = tp_begin->coord;
1899 VikCoord end_coord = tp_end->coord;
1901 gboolean done_start_end = FALSE;
1903 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1904 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1906 // This number can be configured via the settings if you really want to change it
1907 gdouble distance_diff;
1908 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1909 distance_diff = 100.0; // Metres
1911 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1912 // Start and end 'close' together so only draw one label at an average location
1913 gint x1, x2, y1, y2;
1914 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1915 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1917 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1919 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1920 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1923 done_start_end = TRUE;
1927 if ( ! done_start_end ) {
1928 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1929 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1930 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1931 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1932 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1933 g_free ( name_start );
1935 // Don't draw end label if this is the one being created
1936 if ( trk != dp->vtl->current_track ) {
1937 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1938 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1939 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1940 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1941 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1942 g_free ( name_end );
1947 g_free ( fgcolour );
1948 g_free ( bgcolour );
1952 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1954 if ( ! track->visible )
1957 /* TODO: this function is a mess, get rid of any redundancy */
1958 GList *list = track->trackpoints;
1960 gboolean useoldvals = TRUE;
1962 gboolean drawpoints;
1964 gboolean drawelevation;
1965 gdouble min_alt, max_alt, alt_diff = 0;
1967 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1968 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1971 if ( dp->vtl->drawelevation )
1973 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1974 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1975 alt_diff = max_alt - min_alt;
1978 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1979 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1980 trw_layer_draw_track ( id, track, dp, TRUE );
1982 if ( draw_track_outline )
1983 drawpoints = drawstops = FALSE;
1985 drawpoints = dp->vtl->drawpoints;
1986 drawstops = dp->vtl->drawstops;
1989 gboolean drawing_highlight = FALSE;
1990 /* Current track - used for creation */
1991 if ( track == dp->vtl->current_track )
1992 main_gc = dp->vtl->current_track_gc;
1994 if ( dp->highlight ) {
1995 /* Draw all tracks of the layer in special colour
1996 NB this supercedes the drawmode */
1997 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1998 drawing_highlight = TRUE;
2000 if ( !drawing_highlight ) {
2001 // Still need to figure out the gc according to the drawing mode:
2002 switch ( dp->vtl->drawmode ) {
2003 case DRAWMODE_BY_TRACK:
2004 if ( dp->vtl->track_1color_gc )
2005 g_object_unref ( dp->vtl->track_1color_gc );
2006 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2007 main_gc = dp->vtl->track_1color_gc;
2010 // Mostly for DRAWMODE_ALL_SAME_COLOR
2011 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2012 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2019 int x, y, oldx, oldy;
2020 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2022 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2024 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2026 // Draw the first point as something a bit different from the normal points
2027 // ATM it's slightly bigger and a triangle
2029 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2030 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2036 gdouble average_speed = 0.0;
2037 gdouble low_speed = 0.0;
2038 gdouble high_speed = 0.0;
2039 // If necessary calculate these values - which is done only once per track redraw
2040 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2041 // the percentage factor away from the average speed determines transistions between the levels
2042 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2043 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2044 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2047 while ((list = g_list_next(list)))
2049 tp = VIK_TRACKPOINT(list->data);
2050 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2052 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2053 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2054 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2055 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2056 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2058 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2061 * If points are the same in display coordinates, don't draw.
2063 if ( useoldvals && x == oldx && y == oldy )
2065 // Still need to process points to ensure 'stops' are drawn if required
2066 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2067 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2068 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2073 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2074 if ( drawpoints || dp->vtl->drawlines ) {
2075 // setup main_gc for both point and line drawing
2076 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2077 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
2081 if ( drawpoints && ! draw_track_outline )
2086 * The concept of drawing stops is that a trackpoint
2087 * that is if the next trackpoint has a timestamp far into
2088 * the future, we draw a circle of 6x trackpoint size,
2089 * instead of a rectangle of 2x trackpoint size.
2090 * This is drawn first so the trackpoint will be drawn on top
2093 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2094 /* Stop point. Draw 6x circle. Always in redish colour */
2095 vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2097 /* Regular point - draw 2x square. */
2098 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2101 /* Final point - draw 4x circle. */
2102 vik_viewport_draw_arc ( dp->vp, main_gc, TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 );
2105 if ((!tp->newsegment) && (dp->vtl->drawlines))
2108 /* UTM only: zone check */
2109 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2110 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2113 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2115 if ( draw_track_outline ) {
2116 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2120 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2122 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2124 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2129 tmp[1].y = oldy-FIXALTITUDE(list->data);
2131 tmp[2].y = y-FIXALTITUDE(list->next->data);
2136 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2137 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2139 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2140 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2142 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2147 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2148 // Draw an arrow at the mid point to show the direction of the track
2149 // Code is a rework from vikwindow::draw_ruler()
2150 gint midx = (oldx + x) / 2;
2151 gint midy = (oldy + y) / 2;
2153 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2154 // Avoid divide by zero and ensure at least 1 pixel big
2156 gdouble dx = (oldx - midx) / len;
2157 gdouble dy = (oldy - midy) / len;
2158 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2159 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2169 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2171 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2172 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2174 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2176 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2177 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
2181 * If points are the same in display coordinates, don't draw.
2183 if ( x != oldx || y != oldy )
2185 if ( draw_track_outline )
2186 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2188 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2194 * If points are the same in display coordinates, don't draw.
2196 if ( x != oldx && y != oldy )
2198 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2199 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2207 // Labels drawn after the trackpoints, so the labels are on top
2208 if ( dp->vtl->track_draw_labels ) {
2209 if ( track->max_number_dist_labels > 0 ) {
2210 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2213 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2214 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2220 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2222 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2223 trw_layer_draw_track ( id, track, dp, FALSE );
2227 static void cached_pixbuf_free ( CachedPixbuf *cp )
2229 g_object_unref ( G_OBJECT(cp->pixbuf) );
2230 g_free ( cp->image );
2233 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2235 return strcmp ( cp->image, name );
2238 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2241 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2242 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2243 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2246 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2248 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2250 if ( wp->image && dp->vtl->drawimages )
2252 GdkPixbuf *pixbuf = NULL;
2255 if ( dp->vtl->image_alpha == 0)
2258 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2260 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2263 gchar *image = wp->image;
2264 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2265 if ( ! regularthumb )
2267 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2268 image = "\x12\x00"; /* this shouldn't occur naturally. */
2272 CachedPixbuf *cp = NULL;
2273 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2274 if ( dp->vtl->image_size == 128 )
2275 cp->pixbuf = regularthumb;
2278 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2279 g_assert ( cp->pixbuf );
2280 g_object_unref ( G_OBJECT(regularthumb) );
2282 cp->image = g_strdup ( image );
2284 /* needed so 'click picture' tool knows how big the pic is; we don't
2285 * store it in cp because they may have been freed already. */
2286 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2287 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2289 g_queue_push_head ( dp->vtl->image_cache, cp );
2290 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2291 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2293 pixbuf = cp->pixbuf;
2297 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2303 w = gdk_pixbuf_get_width ( pixbuf );
2304 h = gdk_pixbuf_get_height ( pixbuf );
2306 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2308 if ( dp->highlight ) {
2309 // Highlighted - so draw a little border around the chosen one
2310 // single line seems a little weak so draw 2 of them
2311 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2312 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2313 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2314 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2317 if ( dp->vtl->image_alpha == 255 )
2318 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2320 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2322 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2326 // Draw appropriate symbol - either symbol image or simple types
2327 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2328 vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
2330 else if ( wp == dp->vtl->current_wp ) {
2331 switch ( dp->vtl->wp_symbol ) {
2332 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2333 case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2334 case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
2335 case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 );
2336 vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 );
2341 switch ( dp->vtl->wp_symbol ) {
2342 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
2343 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;
2344 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;
2345 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 );
2346 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;
2351 if ( dp->vtl->drawlabels )
2353 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2354 gint label_x, label_y;
2356 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2358 // Could this stored in the waypoint rather than recreating each pass?
2359 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2361 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2362 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2364 // Fallback if parse failure
2365 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2367 g_free ( wp_label_markup );
2369 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2370 label_x = x - width/2;
2371 if ( wp->symbol_pixbuf )
2372 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2374 label_y = y - dp->vtl->wp_size - height - 2;
2376 /* if highlight mode on, then draw background text in highlight colour */
2377 if ( dp->highlight )
2378 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2380 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2381 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2386 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2388 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2389 trw_layer_draw_waypoint ( id, wp, dp );
2393 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2395 static struct DrawingParams dp;
2396 g_assert ( l != NULL );
2398 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2400 if ( l->tracks_visible )
2401 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2403 if ( l->routes_visible )
2404 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2406 if (l->waypoints_visible)
2407 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2410 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2412 // 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
2413 // This may seem slightly inefficient to test each time for every layer
2414 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2415 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2416 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2418 trw_layer_draw_with_highlight ( l, data, FALSE );
2421 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, 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)) )
2426 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2430 * vik_trw_layer_draw_highlight_item:
2432 * Only handles a single track or waypoint ATM
2433 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2435 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2437 // Check the layer for visibility (including all the parents visibilities)
2438 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2441 static struct DrawingParams dp;
2442 init_drawing_params ( &dp, vtl, vvp, TRUE );
2445 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2447 trw_layer_draw_track_cb ( NULL, trk, &dp );
2449 if ( vtl->waypoints_visible && wpt ) {
2450 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2455 * vik_trw_layer_draw_highlight_item:
2457 * Generally for drawing all tracks or routes or waypoints
2458 * trks may be actually routes
2459 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2461 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2463 // Check the layer for visibility (including all the parents visibilities)
2464 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2467 static struct DrawingParams dp;
2468 init_drawing_params ( &dp, vtl, vvp, TRUE );
2471 gboolean is_routes = (trks == vtl->routes);
2472 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2474 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2477 if ( vtl->waypoints_visible && wpts )
2478 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2481 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2484 if ( vtl->track_bg_gc )
2486 g_object_unref ( vtl->track_bg_gc );
2487 vtl->track_bg_gc = NULL;
2489 if ( vtl->track_1color_gc )
2491 g_object_unref ( vtl->track_1color_gc );
2492 vtl->track_1color_gc = NULL;
2494 if ( vtl->current_track_gc )
2496 g_object_unref ( vtl->current_track_gc );
2497 vtl->current_track_gc = NULL;
2499 if ( vtl->current_track_newpoint_gc )
2501 g_object_unref ( vtl->current_track_newpoint_gc );
2502 vtl->current_track_newpoint_gc = NULL;
2505 if ( ! vtl->track_gc )
2507 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2508 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2509 g_array_free ( vtl->track_gc, TRUE );
2510 vtl->track_gc = NULL;
2513 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2515 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2516 gint width = vtl->line_thickness;
2518 if ( vtl->track_gc )
2519 trw_layer_free_track_gcs ( vtl );
2521 if ( vtl->track_bg_gc )
2522 g_object_unref ( vtl->track_bg_gc );
2523 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2525 // Ensure new track drawing heeds line thickness setting
2526 // however always have a minium of 2, as 1 pixel is really narrow
2527 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2529 if ( vtl->current_track_gc )
2530 g_object_unref ( vtl->current_track_gc );
2531 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2532 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2534 // 'newpoint' gc is exactly the same as the current track gc
2535 if ( vtl->current_track_newpoint_gc )
2536 g_object_unref ( vtl->current_track_newpoint_gc );
2537 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2538 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2540 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2542 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2543 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2545 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2546 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2547 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2549 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2551 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2554 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2556 VikTrwLayer *rv = trw_layer_new1 ( vp );
2557 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2559 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2560 /* early exit, as the rest is GUI related */
2564 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2565 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2567 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2568 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2570 trw_layer_new_track_gcs ( rv, vp );
2572 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2573 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2574 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2575 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2577 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2579 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2584 #define SMALL_ICON_SIZE 18
2586 * Can accept a null symbol, and may return null value
2588 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2590 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2591 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2592 // So needing a small icon for the treeview may need some resizing:
2593 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2594 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2598 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2600 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2602 GdkPixbuf *pixbuf = NULL;
2604 if ( track->has_color ) {
2605 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2606 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2607 // Here is some magic found to do the conversion
2608 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2609 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2610 ((track->color.green & 0xff00) << 8) |
2611 (track->color.blue & 0xff00);
2613 gdk_pixbuf_fill ( pixbuf, pixel );
2616 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 );
2619 g_object_unref (pixbuf);
2621 *new_iter = *((GtkTreeIter *) pass_along[1]);
2622 if ( track->is_route )
2623 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2625 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2627 if ( ! track->visible )
2628 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2631 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2633 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2635 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 );
2637 *new_iter = *((GtkTreeIter *) pass_along[1]);
2638 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2640 if ( ! wp->visible )
2641 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2644 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2646 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2649 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2651 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2654 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2656 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2659 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2662 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2664 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2665 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2667 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2669 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2672 if ( g_hash_table_size (vtl->routes) > 0 ) {
2673 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2675 pass_along[0] = &(vtl->routes_iter);
2676 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2678 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2680 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2683 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2684 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2686 pass_along[0] = &(vtl->waypoints_iter);
2687 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2689 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2691 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2696 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2700 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2701 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2702 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2703 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2705 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2707 return (t->visible ^= 1);
2711 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2713 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2715 return (t->visible ^= 1);
2719 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2721 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2723 return (t->visible ^= 1);
2733 * Return a property about tracks for this layer
2735 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2737 return vtl->line_thickness;
2740 // Structure to hold multiple track information for a layer
2749 * Build up layer multiple track information via updating the tooltip_tracks structure
2751 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2753 tt->length = tt->length + vik_track_get_length (tr);
2755 // Ensure times are available
2756 if ( tr->trackpoints &&
2757 vik_track_get_tp_first(tr)->has_timestamp &&
2758 vik_track_get_tp_last(tr)->has_timestamp ) {
2761 t1 = vik_track_get_tp_first(tr)->timestamp;
2762 t2 = vik_track_get_tp_last(tr)->timestamp;
2764 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2765 // Hence initialize to the first 'proper' value
2766 if ( tt->start_time == 0 )
2767 tt->start_time = t1;
2768 if ( tt->end_time == 0 )
2771 // Update find the earliest / last times
2772 if ( t1 < tt->start_time )
2773 tt->start_time = t1;
2774 if ( t2 > tt->end_time )
2777 // Keep track of total time
2778 // there maybe gaps within a track (eg segments)
2779 // but this should be generally good enough for a simple indicator
2780 tt->duration = tt->duration + (int)(t2-t1);
2785 * Generate tooltip text for the layer.
2786 * This is relatively complicated as it considers information for
2787 * no tracks, a single track or multiple tracks
2788 * (which may or may not have timing information)
2790 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2801 static gchar tmp_buf[128];
2804 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2806 // Safety check - I think these should always be valid
2807 if ( vtl->tracks && vtl->waypoints ) {
2808 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2809 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2811 GDate* gdate_start = g_date_new ();
2812 g_date_set_time_t (gdate_start, tt.start_time);
2814 GDate* gdate_end = g_date_new ();
2815 g_date_set_time_t (gdate_end, tt.end_time);
2817 if ( g_date_compare (gdate_start, gdate_end) ) {
2818 // Dates differ so print range on separate line
2819 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2820 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2821 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2824 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2825 if ( tt.start_time != 0 )
2826 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2830 if ( tt.length > 0.0 ) {
2831 gdouble len_in_units;
2833 // Setup info dependent on distance units
2834 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2835 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2836 len_in_units = VIK_METERS_TO_MILES(tt.length);
2839 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2840 len_in_units = tt.length/1000.0;
2843 // Timing information if available
2845 if ( tt.duration > 0 ) {
2846 g_snprintf (tbuf1, sizeof(tbuf1),
2847 _(" in %d:%02d hrs:mins"),
2848 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2850 g_snprintf (tbuf2, sizeof(tbuf2),
2851 _("\n%sTotal Length %.1f %s%s"),
2852 tbuf3, len_in_units, tbuf4, tbuf1);
2855 // Put together all the elements to form compact tooltip text
2856 g_snprintf (tmp_buf, sizeof(tmp_buf),
2857 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2858 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2860 g_date_free (gdate_start);
2861 g_date_free (gdate_end);
2868 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2872 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2874 // Very simple tooltip - may expand detail in the future...
2875 static gchar tmp_buf[32];
2876 g_snprintf (tmp_buf, sizeof(tmp_buf),
2878 g_hash_table_size (l->tracks));
2882 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2884 // Very simple tooltip - may expand detail in the future...
2885 static gchar tmp_buf[32];
2886 g_snprintf (tmp_buf, sizeof(tmp_buf),
2888 g_hash_table_size (l->routes));
2893 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2894 // Same tooltip for a route
2895 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2898 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2899 tr = g_hash_table_lookup ( l->tracks, sublayer );
2901 tr = g_hash_table_lookup ( l->routes, sublayer );
2904 // Could be a better way of handling strings - but this works...
2905 gchar time_buf1[20];
2906 gchar time_buf2[20];
2907 time_buf1[0] = '\0';
2908 time_buf2[0] = '\0';
2909 static gchar tmp_buf[100];
2910 // Compact info: Short date eg (11/20/99), duration and length
2911 // Hopefully these are the things that are most useful and so promoted into the tooltip
2912 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2913 // %x The preferred date representation for the current locale without the time.
2914 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2915 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2916 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2918 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2921 // Get length and consider the appropriate distance units
2922 gdouble tr_len = vik_track_get_length(tr);
2923 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2924 switch (dist_units) {
2925 case VIK_UNITS_DISTANCE_KILOMETRES:
2926 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2928 case VIK_UNITS_DISTANCE_MILES:
2929 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2938 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2940 // Very simple tooltip - may expand detail in the future...
2941 static gchar tmp_buf[32];
2942 g_snprintf (tmp_buf, sizeof(tmp_buf),
2944 g_hash_table_size (l->waypoints));
2948 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2950 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2951 // NB It's OK to return NULL
2956 return w->description;
2965 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2968 * set_statusbar_msg_info_trkpt:
2970 * Function to show track point information on the statusbar
2971 * Items displayed is controlled by the settings format code
2973 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2975 gchar *statusbar_format_code = NULL;
2976 gboolean need2free = FALSE;
2977 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2978 // Otherwise use default
2979 statusbar_format_code = g_strdup ( "KEATDN" );
2983 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2984 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2988 g_free ( statusbar_format_code );
2992 * Function to show basic waypoint information on the statusbar
2994 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2997 switch (a_vik_get_units_height ()) {
2998 case VIK_UNITS_HEIGHT_FEET:
2999 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3002 //VIK_UNITS_HEIGHT_METRES:
3003 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3007 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3008 // one can easily use the current pointer position to see this if needed
3009 gchar *lat = NULL, *lon = NULL;
3010 static struct LatLon ll;
3011 vik_coord_to_latlon (&(wpt->coord), &ll);
3012 a_coords_latlon_to_string ( &ll, &lat, &lon );
3014 // Combine parts to make overall message
3017 // Add comment if available
3018 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3020 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3021 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3028 * General layer selection function, find out which bit is selected and take appropriate action
3030 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3033 l->current_wp = NULL;
3034 l->current_wp_id = NULL;
3035 trw_layer_cancel_current_tp ( l, FALSE );
3038 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3042 case VIK_TREEVIEW_TYPE_LAYER:
3044 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3045 /* Mark for redraw */
3050 case VIK_TREEVIEW_TYPE_SUBLAYER:
3054 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3056 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3057 /* Mark for redraw */
3061 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3063 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3064 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3065 /* Mark for redraw */
3069 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3071 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3072 /* Mark for redraw */
3076 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3078 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3079 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3080 /* Mark for redraw */
3084 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3086 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3087 /* Mark for redraw */
3091 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3093 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3095 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3096 // Show some waypoint info
3097 set_statusbar_msg_info_wpt ( l, wpt );
3098 /* Mark for redraw */
3105 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3114 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3119 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3124 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3129 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3131 return l->waypoints;
3134 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3136 return vtl->tracks_iters;
3139 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3141 return vtl->routes_iters;
3144 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3146 return vtl->waypoints;
3149 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3151 return ! ( g_hash_table_size ( vtl->tracks ) ||
3152 g_hash_table_size ( vtl->routes ) ||
3153 g_hash_table_size ( vtl->waypoints ) );
3156 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3158 return vtl->tracks_visible;
3161 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3163 return vtl->routes_visible;
3166 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3168 return vtl->waypoints_visible;
3172 * ATM use a case sensitive find
3173 * Finds the first one
3175 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3177 if ( wp && wp->name )
3178 if ( ! strcmp ( wp->name, name ) )
3184 * Get waypoint by name - not guaranteed to be unique
3185 * Finds the first one
3187 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3189 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3193 * ATM use a case sensitive find
3194 * Finds the first one
3196 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3198 if ( trk && trk->name )
3199 if ( ! strcmp ( trk->name, name ) )
3205 * Get track by name - not guaranteed to be unique
3206 * Finds the first one
3208 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3210 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3214 * Get route by name - not guaranteed to be unique
3215 * Finds the first one
3217 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3219 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3222 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3224 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3225 maxmin[0].lat = trk->bbox.north;
3226 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3227 maxmin[1].lat = trk->bbox.south;
3228 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3229 maxmin[0].lon = trk->bbox.east;
3230 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3231 maxmin[1].lon = trk->bbox.west;
3234 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3236 // Continually reuse maxmin to find the latest maximum and minimum values
3237 // First set to waypoints bounds
3238 maxmin[0].lat = vtl->waypoints_bbox.north;
3239 maxmin[1].lat = vtl->waypoints_bbox.south;
3240 maxmin[0].lon = vtl->waypoints_bbox.east;
3241 maxmin[1].lon = vtl->waypoints_bbox.west;
3242 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3243 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3246 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3248 /* 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... */
3249 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3250 trw_layer_find_maxmin (vtl, maxmin);
3251 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3255 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3256 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3261 static void trw_layer_centerize ( menu_array_layer values )
3263 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3265 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3266 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3268 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3271 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3273 /* First set the center [in case previously viewing from elsewhere] */
3274 /* Then loop through zoom levels until provided positions are in view */
3275 /* This method is not particularly fast - but should work well enough */
3276 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3278 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3279 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3281 /* Convert into definite 'smallest' and 'largest' positions */
3282 struct LatLon minmin;
3283 if ( maxmin[0].lat < maxmin[1].lat )
3284 minmin.lat = maxmin[0].lat;
3286 minmin.lat = maxmin[1].lat;
3288 struct LatLon maxmax;
3289 if ( maxmin[0].lon > maxmin[1].lon )
3290 maxmax.lon = maxmin[0].lon;
3292 maxmax.lon = maxmin[1].lon;
3294 /* Never zoom in too far - generally not that useful, as too close ! */
3295 /* Always recalculate the 'best' zoom level */
3297 vik_viewport_set_zoom ( vvp, zoom );
3299 gdouble min_lat, max_lat, min_lon, max_lon;
3300 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3301 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3302 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3303 /* NB I think the logic used in this test to determine if the bounds is within view
3304 fails if track goes across 180 degrees longitude.
3305 Hopefully that situation is not too common...
3306 Mind you viking doesn't really do edge locations to well anyway */
3307 if ( min_lat < minmin.lat &&
3308 max_lat > minmin.lat &&
3309 min_lon < maxmax.lon &&
3310 max_lon > maxmax.lon )
3311 /* Found within zoom level */
3316 vik_viewport_set_zoom ( vvp, zoom );
3320 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3322 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3323 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3324 trw_layer_find_maxmin (vtl, maxmin);
3325 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3328 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3333 static void trw_layer_auto_view ( menu_array_layer values )
3335 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3336 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3337 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3338 vik_layers_panel_emit_update ( vlp );
3341 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3344 static void trw_layer_export_gpspoint ( menu_array_layer values )
3346 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3348 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3350 g_free ( auto_save_name );
3353 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3355 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3357 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3359 g_free ( auto_save_name );
3362 static void trw_layer_export_gpx ( menu_array_layer values )
3364 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3366 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3368 g_free ( auto_save_name );
3371 static void trw_layer_export_kml ( menu_array_layer values )
3373 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3375 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3377 g_free ( auto_save_name );
3380 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3382 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3383 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3386 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3388 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3391 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3393 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3396 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3398 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3400 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3401 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3403 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3405 if ( !trk || !trk->name )
3408 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3410 gchar *label = NULL;
3411 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3412 label = _("Export Route as GPX");
3414 label = _("Export Track as GPX");
3415 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3417 g_free ( auto_save_name );
3420 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3422 wpu_udata *user_data = udata;
3423 if ( wp == user_data->wp ) {
3424 user_data->uuid = id;
3430 static void trw_layer_goto_wp ( menu_array_layer values )
3432 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3433 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3434 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3435 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3436 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3438 GTK_RESPONSE_REJECT,
3440 GTK_RESPONSE_ACCEPT,
3443 GtkWidget *label, *entry;
3444 label = gtk_label_new(_("Waypoint Name:"));
3445 entry = gtk_entry_new();
3447 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3448 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3449 gtk_widget_show_all ( label );
3450 gtk_widget_show_all ( entry );
3452 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3454 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3456 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3457 // Find *first* wp with the given name
3458 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3461 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3464 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3465 vik_layers_panel_emit_update ( vlp );
3467 // Find and select on the side panel
3472 // Hmmm, want key of it
3473 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3475 if ( wpf && udata.uuid ) {
3476 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3477 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3486 gtk_widget_destroy ( dia );
3489 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3491 gchar *default_name = highest_wp_number_get(vtl);
3492 VikWaypoint *wp = vik_waypoint_new();
3493 gchar *returned_name;
3495 wp->coord = *def_coord;
3497 // Attempt to auto set height if DEM data is available
3498 vik_waypoint_apply_dem_data ( wp, TRUE );
3500 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3502 if ( returned_name )
3505 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3506 g_free (default_name);
3507 g_free (returned_name);
3510 g_free (default_name);
3511 vik_waypoint_free(wp);
3515 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3517 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3518 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3519 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3520 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3521 VikViewport *vvp = vik_window_viewport(vw);
3523 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3524 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3525 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3526 trw_layer_calculate_bounds_waypoints ( vtl );
3527 vik_layers_panel_emit_update ( vlp );
3530 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3532 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3533 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3534 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3536 trw_layer_find_maxmin (vtl, maxmin);
3537 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3538 trw_layer_calculate_bounds_waypoints ( vtl );
3539 vik_layers_panel_emit_update ( vlp );
3542 #ifdef VIK_CONFIG_GEOTAG
3543 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3545 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3547 // Update directly - not changing the mtime
3548 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3551 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3553 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3556 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3560 * Use code in separate file for this feature as reasonably complex
3562 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3564 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3565 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3566 // Unset so can be reverified later if necessary
3567 vtl->has_verified_thumbnails = FALSE;
3569 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3575 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3577 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3578 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3580 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3586 static void trw_layer_geotagging ( menu_array_layer values )
3588 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3589 // Unset so can be reverified later if necessary
3590 vtl->has_verified_thumbnails = FALSE;
3592 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3599 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3601 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3603 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3604 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3605 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3606 VikViewport *vvp = vik_window_viewport(vw);
3608 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3612 * Acquire into this TRW Layer straight from GPS Device
3614 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3616 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3617 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3621 * Acquire into this TRW Layer from Directions
3623 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3625 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3629 * Acquire into this TRW Layer from an entered URL
3631 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3633 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3634 trw_layer_acquire ( values, &vik_datasource_url_interface );
3637 #ifdef VIK_CONFIG_OPENSTREETMAP
3639 * Acquire into this TRW Layer from OSM
3641 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3643 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3647 * Acquire into this TRW Layer from OSM for 'My' Traces
3649 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3651 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3655 #ifdef VIK_CONFIG_GEOCACHES
3657 * Acquire into this TRW Layer from Geocaching.com
3659 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3661 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3665 #ifdef VIK_CONFIG_GEOTAG
3667 * Acquire into this TRW Layer from images
3669 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3671 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3673 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3674 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3676 // Reverify thumbnails as they may have changed
3677 vtl->has_verified_thumbnails = FALSE;
3678 trw_layer_verify_thumbnails ( vtl, NULL );
3682 static void trw_layer_gps_upload ( menu_array_layer values )
3684 menu_array_sublayer data;
3686 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3688 data[MA_VTL] = values[MA_VTL];
3689 data[MA_VLP] = values[MA_VLP];
3691 trw_layer_gps_upload_any ( data );
3695 * If pass_along[3] is defined that this will upload just that track
3697 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3699 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3700 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3702 // May not actually get a track here as values[2&3] can be null
3703 VikTrack *track = NULL;
3704 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3705 gboolean xfer_all = FALSE;
3707 if ( values[MA_SUBTYPE] ) {
3709 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3710 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3713 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3714 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3717 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3720 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3724 else if ( !values[MA_CONFIRM] )
3725 xfer_all = TRUE; // i.e. whole layer
3727 if (track && !track->visible) {
3728 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3732 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3733 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3734 GTK_DIALOG_DESTROY_WITH_PARENT,
3736 GTK_RESPONSE_ACCEPT,
3738 GTK_RESPONSE_REJECT,
3741 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3742 GtkWidget *response_w = NULL;
3743 #if GTK_CHECK_VERSION (2, 20, 0)
3744 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3748 gtk_widget_grab_focus ( response_w );
3750 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3752 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3753 datasource_gps_clean_up ( dgs );
3754 gtk_widget_destroy ( dialog );
3758 // Get info from reused datasource dialog widgets
3759 gchar* protocol = datasource_gps_get_protocol ( dgs );
3760 gchar* port = datasource_gps_get_descriptor ( dgs );
3761 // NB don't free the above strings as they're references to values held elsewhere
3762 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3763 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3764 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3765 gboolean turn_off = datasource_gps_get_off ( dgs );
3767 gtk_widget_destroy ( dialog );
3769 // When called from the viewport - work the corresponding layerspanel:
3771 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3774 // Apply settings to transfer to the GPS device
3781 vik_layers_panel_get_viewport (vlp),
3790 * Acquire into this TRW Layer from any GPS Babel supported file
3792 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3794 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3795 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3796 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3797 VikViewport *vvp = vik_window_viewport(vw);
3799 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3802 static void trw_layer_new_wp ( menu_array_layer values )
3804 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3805 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3806 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3807 instead return true if you want to update. */
3808 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 ) {
3809 trw_layer_calculate_bounds_waypoints ( vtl );
3810 vik_layers_panel_emit_update ( vlp );
3814 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3816 vtl->current_track = vik_track_new();
3817 vik_track_set_defaults ( vtl->current_track );
3818 vtl->current_track->visible = TRUE;
3819 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3820 // Create track with the preferred colour from the layer properties
3821 vtl->current_track->color = vtl->track_color;
3823 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3824 vtl->current_track->has_color = TRUE;
3825 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3828 static void trw_layer_new_track ( menu_array_layer values )
3830 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3832 if ( ! vtl->current_track ) {
3833 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3834 new_track_create_common ( vtl, name );
3837 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3841 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3843 vtl->current_track = vik_track_new();
3844 vik_track_set_defaults ( vtl->current_track );
3845 vtl->current_track->visible = TRUE;
3846 vtl->current_track->is_route = TRUE;
3847 // By default make all routes red
3848 vtl->current_track->has_color = TRUE;
3849 gdk_color_parse ( "red", &vtl->current_track->color );
3850 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3853 static void trw_layer_new_route ( menu_array_layer values )
3855 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3857 if ( ! vtl->current_track ) {
3858 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3859 new_route_create_common ( vtl, name );
3861 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3865 static void trw_layer_auto_routes_view ( menu_array_layer values )
3867 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3868 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3870 if ( g_hash_table_size (vtl->routes) > 0 ) {
3871 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3872 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3873 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3874 vik_layers_panel_emit_update ( vlp );
3879 static void trw_layer_finish_track ( menu_array_layer values )
3881 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3882 vtl->current_track = NULL;
3883 vik_layer_emit_update ( VIK_LAYER(vtl) );
3886 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3888 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3889 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3891 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3892 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3893 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3894 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3895 vik_layers_panel_emit_update ( vlp );
3899 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3901 /* NB do not care if wp is visible or not */
3902 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3905 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3907 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3908 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3910 /* Only 1 waypoint - jump straight to it */
3911 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3912 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3913 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3915 /* If at least 2 waypoints - find center and then zoom to fit */
3916 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3918 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3919 maxmin[0].lat = vtl->waypoints_bbox.north;
3920 maxmin[1].lat = vtl->waypoints_bbox.south;
3921 maxmin[0].lon = vtl->waypoints_bbox.east;
3922 maxmin[1].lon = vtl->waypoints_bbox.west;
3923 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3926 vik_layers_panel_emit_update ( vlp );
3929 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3931 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3934 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3936 if ( values[MA_MISC] ) {
3937 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3938 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3942 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3944 static menu_array_layer pass_along;
3946 GtkWidget *export_submenu;
3947 pass_along[MA_VTL] = vtl;
3948 pass_along[MA_VLP] = vlp;
3950 item = gtk_menu_item_new();
3951 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3952 gtk_widget_show ( item );
3954 if ( vtl->current_track ) {
3955 if ( vtl->current_track->is_route )
3956 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3958 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3961 gtk_widget_show ( item );
3964 item = gtk_menu_item_new ();
3965 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3966 gtk_widget_show ( item );
3969 /* Now with icons */
3970 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3973 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3974 gtk_widget_show ( item );
3976 GtkWidget *view_submenu = gtk_menu_new();
3977 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3979 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3980 gtk_widget_show ( item );
3981 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3983 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3985 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3986 gtk_widget_show ( item );
3988 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3990 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3991 gtk_widget_show ( item );
3993 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3995 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3996 gtk_widget_show ( item );
3998 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3999 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4001 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4002 gtk_widget_show ( item );
4004 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4006 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4007 gtk_widget_show ( item );
4009 export_submenu = gtk_menu_new ();
4010 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4011 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4012 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4013 gtk_widget_show ( item );
4014 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4016 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4018 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4019 gtk_widget_show ( item );
4021 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4023 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4024 gtk_widget_show ( item );
4026 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4028 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4029 gtk_widget_show ( item );
4031 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4033 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4034 gtk_widget_show ( item );
4036 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4038 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4039 gtk_widget_show ( item );
4041 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4042 item = gtk_menu_item_new_with_mnemonic ( external1 );
4043 g_free ( external1 );
4044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4045 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4046 gtk_widget_show ( item );
4048 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4049 item = gtk_menu_item_new_with_mnemonic ( external2 );
4050 g_free ( external2 );
4051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4052 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4053 gtk_widget_show ( item );
4055 GtkWidget *new_submenu = gtk_menu_new();
4056 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4057 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4058 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4059 gtk_widget_show(item);
4060 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4062 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4063 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4065 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4066 gtk_widget_show ( item );
4068 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4071 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4072 gtk_widget_show ( item );
4073 // Make it available only when a new track *not* already in progress
4074 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4076 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4077 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4079 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4080 gtk_widget_show ( item );
4081 // Make it available only when a new track *not* already in progress
4082 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4084 #ifdef VIK_CONFIG_GEOTAG
4085 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4087 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4088 gtk_widget_show ( item );
4091 GtkWidget *acquire_submenu = gtk_menu_new ();
4092 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4093 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4094 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4095 gtk_widget_show ( item );
4096 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4098 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4100 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4101 gtk_widget_show ( item );
4103 /* FIXME: only add menu when at least a routing engine has support for Directions */
4104 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4106 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4107 gtk_widget_show ( item );
4109 #ifdef VIK_CONFIG_OPENSTREETMAP
4110 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4111 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4112 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4113 gtk_widget_show ( item );
4115 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4116 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4117 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4118 gtk_widget_show ( item );
4121 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4123 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4124 gtk_widget_show ( item );
4126 #ifdef VIK_CONFIG_GEONAMES
4127 GtkWidget *wikipedia_submenu = gtk_menu_new();
4128 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4129 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4130 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4131 gtk_widget_show(item);
4132 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4134 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4137 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4138 gtk_widget_show ( item );
4140 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4144 gtk_widget_show ( item );
4147 #ifdef VIK_CONFIG_GEOCACHES
4148 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4150 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4151 gtk_widget_show ( item );
4154 #ifdef VIK_CONFIG_GEOTAG
4155 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4157 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4158 gtk_widget_show ( item );
4161 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4163 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4164 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4165 gtk_widget_show ( item );
4167 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4169 GtkWidget *upload_submenu = gtk_menu_new ();
4170 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4171 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4172 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4173 gtk_widget_show ( item );
4174 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4176 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4179 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4180 gtk_widget_show ( item );
4182 #ifdef VIK_CONFIG_OPENSTREETMAP
4183 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4184 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4186 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4187 gtk_widget_show ( item );
4190 GtkWidget *delete_submenu = gtk_menu_new ();
4191 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4192 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4193 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4194 gtk_widget_show ( item );
4195 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4197 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4198 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4200 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4201 gtk_widget_show ( item );
4203 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4204 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4206 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4207 gtk_widget_show ( item );
4209 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4210 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4211 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4212 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4213 gtk_widget_show ( item );
4215 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4216 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4217 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4218 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4219 gtk_widget_show ( item );
4221 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4222 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4223 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4224 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4225 gtk_widget_show ( item );
4227 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4229 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4230 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4231 gtk_widget_show ( item );
4233 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4234 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4236 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4237 gtk_widget_show ( item );
4240 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4241 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4243 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4244 gtk_widget_show ( item );
4247 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4248 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4250 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4251 gtk_widget_show ( item );
4252 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4254 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4255 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4256 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4257 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4258 gtk_widget_show ( item );
4259 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4262 // Fake Waypoint UUIDs vi simple increasing integer
4263 static guint wp_uuid = 0;
4265 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4269 vik_waypoint_set_name (wp, name);
4271 if ( VIK_LAYER(vtl)->realized )
4273 // Do we need to create the sublayer:
4274 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4275 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4278 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4280 // Visibility column always needed for waypoints
4281 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 );
4283 // Actual setting of visibility dependent on the waypoint
4284 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4286 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4288 // Sort now as post_read is not called on a realized waypoint
4289 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4292 highest_wp_number_add_wp(vtl, name);
4293 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4297 // Fake Track UUIDs vi simple increasing integer
4298 static guint tr_uuid = 0;
4300 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4304 vik_track_set_name (t, name);
4306 if ( VIK_LAYER(vtl)->realized )
4308 // Do we need to create the sublayer:
4309 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4310 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4313 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4314 // Visibility column always needed for tracks
4315 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 );
4317 // Actual setting of visibility dependent on the track
4318 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4320 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4322 // Sort now as post_read is not called on a realized track
4323 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4326 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4328 trw_layer_update_treeview ( vtl, t );
4331 // Fake Route UUIDs vi simple increasing integer
4332 static guint rt_uuid = 0;
4334 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4338 vik_track_set_name (t, name);
4340 if ( VIK_LAYER(vtl)->realized )
4342 // Do we need to create the sublayer:
4343 if ( g_hash_table_size (vtl->routes) == 0 ) {
4344 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4347 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4348 // Visibility column always needed for routes
4349 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 );
4350 // Actual setting of visibility dependent on the route
4351 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4353 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4355 // Sort now as post_read is not called on a realized route
4356 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4359 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4361 trw_layer_update_treeview ( vtl, t );
4364 /* to be called whenever a track has been deleted or may have been changed. */
4365 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4367 if (vtl->current_tp_track == trk )
4368 trw_layer_cancel_current_tp ( vtl, FALSE );
4372 * Normally this is done to due the waypoint size preference having changed
4374 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4376 GHashTableIter iter;
4377 gpointer key, value;
4380 g_hash_table_iter_init ( &iter, vtl->waypoints );
4381 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4382 VikWaypoint *wp = VIK_WAYPOINT(value);
4384 // Reapply symbol setting to update the pixbuf
4385 gchar *tmp_symbol = g_strdup ( wp->symbol );
4386 vik_waypoint_set_symbol ( wp, tmp_symbol );
4387 g_free ( tmp_symbol );
4393 * trw_layer_new_unique_sublayer_name:
4395 * Allocates a unique new name
4397 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4400 gchar *newname = g_strdup(name);
4405 switch ( sublayer_type ) {
4406 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4407 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4409 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4410 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4413 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4416 // If found a name already in use try adding 1 to it and we try again
4418 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4420 newname = new_newname;
4423 } while ( id != NULL);
4428 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4430 // No more uniqueness of name forced when loading from a file
4431 // This now makes this function a little redunant as we just flow the parameters through
4432 vik_trw_layer_add_waypoint ( vtl, name, wp );
4435 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4437 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4438 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4439 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4440 vik_track_free ( tr );
4441 vtl->route_finder_append = FALSE; /* this means we have added it */
4444 // No more uniqueness of name forced when loading from a file
4446 vik_trw_layer_add_route ( vtl, name, tr );
4448 vik_trw_layer_add_track ( vtl, name, tr );
4450 if ( vtl->route_finder_check_added_track ) {
4451 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4452 vtl->route_finder_added_track = tr;
4457 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4459 *l = g_list_append(*l, id);
4463 * Move an item from one TRW layer to another TRW layer
4465 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4467 // TODO reconsider strategy when moving within layer (if anything...)
4468 gboolean rename = ( vtl_src != vtl_dest );
4472 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4473 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4477 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4479 newname = g_strdup ( trk->name );
4481 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4482 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4484 vik_trw_layer_delete_track ( vtl_src, trk );
4487 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4488 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4492 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4494 newname = g_strdup ( trk->name );
4496 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4497 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4499 vik_trw_layer_delete_route ( vtl_src, trk );
4502 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4503 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4507 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4509 newname = g_strdup ( wp->name );
4511 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4512 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4514 trw_layer_delete_waypoint ( vtl_src, wp );
4516 // Recalculate bounds even if not renamed as maybe dragged between layers
4517 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4518 trw_layer_calculate_bounds_waypoints ( vtl_src );
4522 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4524 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4525 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4527 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4528 GList *items = NULL;
4531 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4532 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4534 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4535 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4537 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4538 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4543 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4544 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4545 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4546 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4548 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4555 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4556 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4560 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4562 trku_udata *user_data = udata;
4563 if ( trk == user_data->trk ) {
4564 user_data->uuid = id;
4570 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4572 gboolean was_visible = FALSE;
4573 if ( trk && trk->name ) {
4575 if ( trk == vtl->current_track ) {
4576 vtl->current_track = NULL;
4577 vtl->current_tp_track = NULL;
4578 vtl->current_tp_id = NULL;
4579 vtl->moving_tp = FALSE;
4582 was_visible = trk->visible;
4584 if ( trk == vtl->route_finder_current_track )
4585 vtl->route_finder_current_track = NULL;
4587 if ( trk == vtl->route_finder_added_track )
4588 vtl->route_finder_added_track = NULL;
4594 // Hmmm, want key of it
4595 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4597 if ( trkf && udata.uuid ) {
4598 /* could be current_tp, so we have to check */
4599 trw_layer_cancel_tps_of_track ( vtl, trk );
4601 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4604 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4605 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4606 g_hash_table_remove ( vtl->tracks, udata.uuid );
4608 // If last sublayer, then remove sublayer container
4609 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4610 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4613 // Incase it was selected (no item delete signal ATM)
4614 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4620 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4622 gboolean was_visible = FALSE;
4624 if ( trk && trk->name ) {
4626 if ( trk == vtl->current_track ) {
4627 vtl->current_track = NULL;
4628 vtl->current_tp_track = NULL;
4629 vtl->current_tp_id = NULL;
4630 vtl->moving_tp = FALSE;
4633 was_visible = trk->visible;
4635 if ( trk == vtl->route_finder_current_track )
4636 vtl->route_finder_current_track = NULL;
4638 if ( trk == vtl->route_finder_added_track )
4639 vtl->route_finder_added_track = NULL;
4645 // Hmmm, want key of it
4646 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4648 if ( trkf && udata.uuid ) {
4649 /* could be current_tp, so we have to check */
4650 trw_layer_cancel_tps_of_track ( vtl, trk );
4652 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4655 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4656 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4657 g_hash_table_remove ( vtl->routes, udata.uuid );
4659 // If last sublayer, then remove sublayer container
4660 if ( g_hash_table_size (vtl->routes) == 0 ) {
4661 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4664 // Incase it was selected (no item delete signal ATM)
4665 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4671 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4673 gboolean was_visible = FALSE;
4675 if ( wp && wp->name ) {
4677 if ( wp == vtl->current_wp ) {
4678 vtl->current_wp = NULL;
4679 vtl->current_wp_id = NULL;
4680 vtl->moving_wp = FALSE;
4683 was_visible = wp->visible;
4689 // Hmmm, want key of it
4690 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4692 if ( wpf && udata.uuid ) {
4693 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4696 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4697 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4699 highest_wp_number_remove_wp(vtl, wp->name);
4700 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4702 // If last sublayer, then remove sublayer container
4703 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4704 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4707 // Incase it was selected (no item delete signal ATM)
4708 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4716 // Only for temporary use by trw_layer_delete_waypoint_by_name
4717 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4719 wpu_udata *user_data = udata;
4720 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4721 user_data->uuid = id;
4728 * Delete a waypoint by the given name
4729 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4730 * as there be multiple waypoints with the same name
4732 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4735 // Fake a waypoint with the given name
4736 udata.wp = vik_waypoint_new ();
4737 vik_waypoint_set_name (udata.wp, name);
4738 // Currently only the name is used in this waypoint find function
4741 // Hmmm, want key of it
4742 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4744 vik_waypoint_free (udata.wp);
4746 if ( wpf && udata.uuid )
4747 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4753 VikTrack *trk; // input
4754 gpointer uuid; // output
4757 // Only for temporary use by trw_layer_delete_track_by_name
4758 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4760 tpu_udata *user_data = udata;
4761 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4762 user_data->uuid = id;
4769 * Delete a track by the given name
4770 * NOTE: ATM this will delete the first encountered Track with the specified name
4771 * as there may be multiple tracks with the same name within the specified hash table
4773 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4776 // Fake a track with the given name
4777 udata.trk = vik_track_new ();
4778 vik_track_set_name (udata.trk, name);
4779 // Currently only the name is used in this waypoint find function
4782 // Hmmm, want key of it
4783 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4785 vik_track_free (udata.trk);
4787 if ( trkf && udata.uuid ) {
4788 // This could be a little better written...
4789 if ( vtl->tracks == ht_tracks )
4790 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4791 if ( vtl->routes == ht_tracks )
4792 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4799 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4801 vik_treeview_item_delete (vt, it );
4804 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4807 vtl->current_track = NULL;
4808 vtl->route_finder_current_track = NULL;
4809 vtl->route_finder_added_track = NULL;
4810 if (vtl->current_tp_track)
4811 trw_layer_cancel_current_tp(vtl, FALSE);
4813 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4814 g_hash_table_remove_all(vtl->routes_iters);
4815 g_hash_table_remove_all(vtl->routes);
4817 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4819 vik_layer_emit_update ( VIK_LAYER(vtl) );
4822 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4825 vtl->current_track = NULL;
4826 vtl->route_finder_current_track = NULL;
4827 vtl->route_finder_added_track = NULL;
4828 if (vtl->current_tp_track)
4829 trw_layer_cancel_current_tp(vtl, FALSE);
4831 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4832 g_hash_table_remove_all(vtl->tracks_iters);
4833 g_hash_table_remove_all(vtl->tracks);
4835 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4837 vik_layer_emit_update ( VIK_LAYER(vtl) );
4840 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4842 vtl->current_wp = NULL;
4843 vtl->current_wp_id = NULL;
4844 vtl->moving_wp = FALSE;
4846 highest_wp_number_reset(vtl);
4848 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4849 g_hash_table_remove_all(vtl->waypoints_iters);
4850 g_hash_table_remove_all(vtl->waypoints);
4852 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4854 vik_layer_emit_update ( VIK_LAYER(vtl) );
4857 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4859 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4860 // Get confirmation from the user
4861 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4862 _("Are you sure you want to delete all tracks in %s?"),
4863 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4864 vik_trw_layer_delete_all_tracks (vtl);
4867 static void trw_layer_delete_all_routes ( menu_array_layer values )
4869 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4870 // Get confirmation from the user
4871 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4872 _("Are you sure you want to delete all routes in %s?"),
4873 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4874 vik_trw_layer_delete_all_routes (vtl);
4877 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4879 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4880 // Get confirmation from the user
4881 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4882 _("Are you sure you want to delete all waypoints in %s?"),
4883 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4884 vik_trw_layer_delete_all_waypoints (vtl);
4887 static void trw_layer_delete_item ( menu_array_sublayer values )
4889 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4890 gboolean was_visible = FALSE;
4891 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4893 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4894 if ( wp && wp->name ) {
4895 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4896 // Get confirmation from the user
4897 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4898 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4899 _("Are you sure you want to delete the waypoint \"%s\"?"),
4902 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4905 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4907 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4908 if ( trk && trk->name ) {
4909 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4910 // Get confirmation from the user
4911 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4912 _("Are you sure you want to delete the track \"%s\"?"),
4915 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4920 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4921 if ( trk && trk->name ) {
4922 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4923 // Get confirmation from the user
4924 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4925 _("Are you sure you want to delete the route \"%s\"?"),
4928 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4932 vik_layer_emit_update ( VIK_LAYER(vtl) );
4936 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4938 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4940 vik_waypoint_set_name ( wp, new_name );
4942 // Now update the treeview as well
4947 // Need key of it for treeview update
4948 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4950 if ( wpf && udataU.uuid ) {
4951 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4954 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4955 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4961 * Maintain icon of waypoint in the treeview
4963 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4965 // update the treeview
4970 // Need key of it for treeview update
4971 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4973 if ( wpf && udataU.uuid ) {
4974 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4977 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4982 static void trw_layer_properties_item ( menu_array_sublayer values )
4984 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4985 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4987 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4989 if ( wp && wp->name )
4991 gboolean updated = FALSE;
4992 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4994 trw_layer_waypoint_rename ( vtl, wp, new_name );
4996 if ( updated && values[MA_TV_ITER] )
4997 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4999 if ( updated && VIK_LAYER(vtl)->visible )
5000 vik_layer_emit_update ( VIK_LAYER(vtl) );
5006 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5007 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5009 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5011 if ( tr && tr->name )
5013 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5024 * trw_layer_track_statistics:
5026 * Show track statistics.
5027 * ATM jump to the stats page in the properties
5028 * TODO: consider separating the stats into an individual dialog?
5030 static void trw_layer_track_statistics ( menu_array_sublayer values )
5032 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5034 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5035 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5037 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5039 if ( trk && trk->name ) {
5040 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5050 * Update the treeview of the track id - primarily to update the icon
5052 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5058 gpointer *trkf = NULL;
5059 if ( trk->is_route )
5060 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5062 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5064 if ( trkf && udata.uuid ) {
5066 GtkTreeIter *iter = NULL;
5067 if ( trk->is_route )
5068 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5070 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5073 // TODO: Make this a function
5074 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5075 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5076 ((trk->color.green & 0xff00) << 8) |
5077 (trk->color.blue & 0xff00);
5078 gdk_pixbuf_fill ( pixbuf, pixel );
5079 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5080 g_object_unref (pixbuf);
5087 Parameter 1 -> VikLayersPanel
5088 Parameter 2 -> VikLayer
5089 Parameter 3 -> VikViewport
5091 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5094 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5095 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5098 /* since vlp not set, vl & vvp should be valid instead! */
5100 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5101 vik_layer_emit_update ( VIK_LAYER(vl) );
5106 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5108 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5110 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5111 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5113 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5115 if ( track && track->trackpoints )
5116 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5119 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5121 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5123 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5124 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5126 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5128 if ( track && track->trackpoints )
5130 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5132 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5133 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5134 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5135 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5136 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5140 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5142 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5144 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5145 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5147 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5152 // Converting a track to a route can be a bit more complicated,
5153 // so give a chance to change our minds:
5154 if ( !trk->is_route &&
5155 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5156 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5158 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5159 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5164 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5167 trk_copy->is_route = !trk_copy->is_route;
5169 // ATM can't set name to self - so must create temporary copy
5170 gchar *name = g_strdup ( trk_copy->name );
5172 // Delete old one and then add new one
5173 if ( trk->is_route ) {
5174 vik_trw_layer_delete_route ( vtl, trk );
5175 vik_trw_layer_add_track ( vtl, name, trk_copy );
5178 // Extra route conversion bits...
5179 vik_track_merge_segments ( trk_copy );
5180 vik_track_to_routepoints ( trk_copy );
5182 vik_trw_layer_delete_track ( vtl, trk );
5183 vik_trw_layer_add_route ( vtl, name, trk_copy );
5187 // Update in case color of track / route changes when moving between sublayers
5188 vik_layer_emit_update ( VIK_LAYER(vtl) );
5191 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5193 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5195 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5196 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5198 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5201 vik_track_anonymize_times ( track );
5204 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5206 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5208 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5209 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5211 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5216 vtl->current_track = track;
5217 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);
5219 if ( track->trackpoints )
5220 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5224 * extend a track using route finder
5226 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5228 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5229 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5232 if ( !track->trackpoints )
5235 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5236 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5237 vtl->route_finder_current_track = track;
5238 vtl->route_finder_started = TRUE;
5240 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5246 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5248 // If have a vlp then perform a basic test to see if any DEM info available...
5250 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5252 if ( !g_list_length(dems) ) {
5253 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5261 * apply_dem_data_common:
5263 * A common function for applying the DEM values and reporting the results.
5265 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5267 if ( !trw_layer_dem_test ( vtl, vlp ) )
5270 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
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);
5278 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5280 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5282 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5283 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5285 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5288 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5291 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5293 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5295 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5296 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5298 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5301 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5307 * A common function for applying the elevation smoothing and reporting the results.
5309 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5311 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5312 // Inform user how much was changed
5314 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5315 g_snprintf(str, 64, tmp_str, changed);
5316 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5322 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5324 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5326 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5327 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5329 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5334 smooth_it ( vtl, track, FALSE );
5337 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5339 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5341 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5342 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5344 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5349 smooth_it ( vtl, track, TRUE );
5353 * Commonal helper function
5355 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5358 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5359 g_snprintf(str, 64, tmp_str, changed);
5360 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5363 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5365 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5366 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5368 if ( !trw_layer_dem_test ( vtl, vlp ) )
5372 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5374 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5376 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5380 GHashTableIter iter;
5381 gpointer key, value;
5383 g_hash_table_iter_init ( &iter, vtl->waypoints );
5384 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5385 VikWaypoint *wp = VIK_WAYPOINT(value);
5386 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5389 wp_changed_message ( vtl, changed );
5392 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5394 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5395 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5397 if ( !trw_layer_dem_test ( vtl, vlp ) )
5401 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5403 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5405 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5409 GHashTableIter iter;
5410 gpointer key, value;
5412 g_hash_table_iter_init ( &iter, vtl->waypoints );
5413 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5414 VikWaypoint *wp = VIK_WAYPOINT(value);
5415 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5418 wp_changed_message ( vtl, changed );
5421 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5423 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5425 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5426 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5428 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5432 if ( !track->trackpoints )
5434 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5437 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5439 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5441 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5442 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5444 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5449 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5452 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5455 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5457 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5459 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5460 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5462 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5467 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5470 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5473 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5475 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5477 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5478 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5480 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5485 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5488 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5492 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5494 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5496 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5498 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5499 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5501 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5503 if ( trk && trk->trackpoints )
5505 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5506 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5507 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5508 if ( values[MA_VLP] )
5509 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5511 vik_layer_emit_update ( VIK_LAYER(vtl) );
5516 * Refine the selected track/route with a routing engine.
5517 * The routing engine is selected by the user, when requestiong the job.
5519 static void trw_layer_route_refine ( menu_array_sublayer values )
5521 static gint last_engine = 0;
5522 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5525 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5526 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5528 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5530 if ( trk && trk->trackpoints )
5532 /* Check size of the route */
5533 int nb = vik_track_get_tp_count(trk);
5535 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5536 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5537 GTK_MESSAGE_WARNING,
5538 GTK_BUTTONS_OK_CANCEL,
5539 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5541 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5542 gtk_widget_destroy ( dialog );
5543 if (response != GTK_RESPONSE_OK )
5546 /* Select engine from dialog */
5547 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5548 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5549 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5551 GTK_RESPONSE_REJECT,
5553 GTK_RESPONSE_ACCEPT,
5555 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5556 gtk_widget_show_all(label);
5558 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5560 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5561 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5562 gtk_widget_show_all(combo);
5564 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5566 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5568 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5570 /* Dialog validated: retrieve selected engine and do the job */
5571 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5572 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5575 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5577 /* Force saving track */
5578 /* FIXME: remove or rename this hack */
5579 vtl->route_finder_check_added_track = TRUE;
5582 vik_routing_engine_refine (routing, vtl, trk);
5584 /* FIXME: remove or rename this hack */
5585 if ( vtl->route_finder_added_track )
5586 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5588 vtl->route_finder_added_track = NULL;
5589 vtl->route_finder_check_added_track = FALSE;
5591 vik_layer_emit_update ( VIK_LAYER(vtl) );
5593 /* Restore cursor */
5594 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5596 gtk_widget_destroy ( dialog );
5600 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5602 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5603 trw_layer_tpwin_init ( vtl );
5606 /*************************************
5607 * merge/split by time routines
5608 *************************************/
5610 /* called for each key in track hash table.
5611 * If the current track has the same time stamp type, add it to the result,
5612 * except the one pointed by "exclude".
5613 * set exclude to NULL if there is no exclude to check.
5614 * Note that the result is in reverse (for performance reasons).
5619 gboolean with_timestamps;
5621 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5623 twt_udata *user_data = udata;
5624 VikTrackpoint *p1, *p2;
5625 VikTrack *trk = VIK_TRACK(value);
5626 if (trk == user_data->exclude) {
5630 if (trk->trackpoints) {
5631 p1 = vik_track_get_tp_first(trk);
5632 p2 = vik_track_get_tp_last(trk);
5634 if ( user_data->with_timestamps ) {
5635 if (!p1->has_timestamp || !p2->has_timestamp) {
5640 // Don't add tracks with timestamps when getting non timestamp tracks
5641 if (p1->has_timestamp || p2->has_timestamp) {
5647 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5650 /* called for each key in track hash table. if original track user_data[1] is close enough
5651 * to the passed one, add it to list in user_data[0]
5653 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5656 VikTrackpoint *p1, *p2;
5657 VikTrack *trk = VIK_TRACK(value);
5659 GList **nearby_tracks = ((gpointer *)user_data)[0];
5662 * detect reasons for not merging, and return
5663 * if no reason is found not to merge, then do it.
5666 twt_udata *udata = user_data;
5667 // Exclude the original track from the compiled list
5668 if (trk == udata->exclude) {
5672 t1 = vik_track_get_tp_first(trk)->timestamp;
5673 t2 = vik_track_get_tp_last(trk)->timestamp;
5675 if (trk->trackpoints) {
5676 p1 = vik_track_get_tp_first(trk);
5677 p2 = vik_track_get_tp_last(trk);
5679 if (!p1->has_timestamp || !p2->has_timestamp) {
5680 //g_print("no timestamp\n");
5684 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5685 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5686 if (! (abs(t1 - p2->timestamp) < threshold ||
5688 abs(p1->timestamp - t2) < threshold)
5695 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5698 /* comparison function used to sort tracks; a and b are hash table keys */
5699 /* Not actively used - can be restored if needed
5700 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5702 GHashTable *tracks = user_data;
5705 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5706 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5708 if (t1 < t2) return -1;
5709 if (t1 > t2) return 1;
5714 /* comparison function used to sort trackpoints */
5715 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5717 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5719 if (t1 < t2) return -1;
5720 if (t1 > t2) return 1;
5725 * comparison function which can be used to sort tracks or waypoints by name
5727 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5729 const gchar* namea = (const gchar*) a;
5730 const gchar* nameb = (const gchar*) b;
5731 if ( namea == NULL || nameb == NULL)
5734 // Same sort method as used in the vik_treeview_*_alphabetize functions
5735 return strcmp ( namea, nameb );
5739 * Attempt to merge selected track with other tracks specified by the user
5740 * Tracks to merge with must be of the same 'type' as the selected track -
5741 * either all with timestamps, or all without timestamps
5743 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5745 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5746 GList *other_tracks = NULL;
5747 GHashTable *ght_tracks;
5748 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5749 ght_tracks = vtl->routes;
5751 ght_tracks = vtl->tracks;
5753 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5758 if ( !track->trackpoints )
5762 udata.result = &other_tracks;
5763 udata.exclude = track;
5764 // Allow merging with 'similar' time type time tracks
5765 // i.e. either those times, or those without
5766 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5768 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5769 other_tracks = g_list_reverse(other_tracks);
5771 if ( !other_tracks ) {
5772 if ( udata.with_timestamps )
5773 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5775 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5779 // Sort alphabetically for user presentation
5780 // Convert into list of names for usage with dialog function
5781 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5782 GList *other_tracks_names = NULL;
5783 GList *iter = g_list_first ( other_tracks );
5785 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5786 iter = g_list_next ( iter );
5789 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5791 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5795 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5796 g_list_free(other_tracks);
5797 g_list_free(other_tracks_names);
5802 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5803 VikTrack *merge_track;
5804 if ( track->is_route )
5805 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5807 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5810 vik_track_steal_and_append_trackpoints ( track, merge_track );
5811 if ( track->is_route )
5812 vik_trw_layer_delete_route (vtl, merge_track);
5814 vik_trw_layer_delete_track (vtl, merge_track);
5815 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5818 for (l = merge_list; l != NULL; l = g_list_next(l))
5820 g_list_free(merge_list);
5822 vik_layer_emit_update( VIK_LAYER(vtl) );
5826 // c.f. trw_layer_sorted_track_id_by_name_list
5827 // but don't add the specified track to the list (normally current track)
5828 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5830 twt_udata *user_data = udata;
5833 if (trk == user_data->exclude) {
5837 // Sort named list alphabetically
5838 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5842 * Join - this allows combining 'tracks' and 'track routes'
5843 * i.e. doesn't care about whether tracks have consistent timestamps
5844 * ATM can only append one track at a time to the currently selected track
5846 static void trw_layer_append_track ( menu_array_sublayer values )
5849 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5851 GHashTable *ght_tracks;
5852 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5853 ght_tracks = vtl->routes;
5855 ght_tracks = vtl->tracks;
5857 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5862 GList *other_tracks_names = NULL;
5864 // Sort alphabetically for user presentation
5865 // Convert into list of names for usage with dialog function
5866 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5868 udata.result = &other_tracks_names;
5869 udata.exclude = trk;
5871 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5873 // Note the limit to selecting one track only
5874 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5875 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5876 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5879 trk->is_route ? _("Append Route"): _("Append Track"),
5880 trk->is_route ? _("Select the route to append after the current route") :
5881 _("Select the track to append after the current track") );
5883 g_list_free(other_tracks_names);
5885 // It's a list, but shouldn't contain more than one other track!
5886 if ( append_list ) {
5888 for (l = append_list; l != NULL; l = g_list_next(l)) {
5889 // TODO: at present this uses the first track found by name,
5890 // which with potential multiple same named tracks may not be the one selected...
5891 VikTrack *append_track;
5892 if ( trk->is_route )
5893 append_track = vik_trw_layer_get_route ( vtl, l->data );
5895 append_track = vik_trw_layer_get_track ( vtl, l->data );
5897 if ( append_track ) {
5898 vik_track_steal_and_append_trackpoints ( trk, append_track );
5899 if ( trk->is_route )
5900 vik_trw_layer_delete_route (vtl, append_track);
5902 vik_trw_layer_delete_track (vtl, append_track);
5905 for (l = append_list; l != NULL; l = g_list_next(l))
5907 g_list_free(append_list);
5909 vik_layer_emit_update( VIK_LAYER(vtl) );
5914 * Very similar to trw_layer_append_track for joining
5915 * but this allows selection from the 'other' list
5916 * If a track is selected, then is shows routes and joins the selected one
5917 * If a route is selected, then is shows tracks and joins the selected one
5919 static void trw_layer_append_other ( menu_array_sublayer values )
5922 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5924 GHashTable *ght_mykind, *ght_others;
5925 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5926 ght_mykind = vtl->routes;
5927 ght_others = vtl->tracks;
5930 ght_mykind = vtl->tracks;
5931 ght_others = vtl->routes;
5934 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5939 GList *other_tracks_names = NULL;
5941 // Sort alphabetically for user presentation
5942 // Convert into list of names for usage with dialog function
5943 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5945 udata.result = &other_tracks_names;
5946 udata.exclude = trk;
5948 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5950 // Note the limit to selecting one track only
5951 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5952 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5953 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5956 trk->is_route ? _("Append Track"): _("Append Route"),
5957 trk->is_route ? _("Select the track to append after the current route") :
5958 _("Select the route to append after the current track") );
5960 g_list_free(other_tracks_names);
5962 // It's a list, but shouldn't contain more than one other track!
5963 if ( append_list ) {
5965 for (l = append_list; l != NULL; l = g_list_next(l)) {
5966 // TODO: at present this uses the first track found by name,
5967 // which with potential multiple same named tracks may not be the one selected...
5969 // Get FROM THE OTHER TYPE list
5970 VikTrack *append_track;
5971 if ( trk->is_route )
5972 append_track = vik_trw_layer_get_track ( vtl, l->data );
5974 append_track = vik_trw_layer_get_route ( vtl, l->data );
5976 if ( append_track ) {
5978 if ( !append_track->is_route &&
5979 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5980 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5982 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5983 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5984 vik_track_merge_segments ( append_track );
5985 vik_track_to_routepoints ( append_track );
5992 vik_track_steal_and_append_trackpoints ( trk, append_track );
5994 // Delete copied which is FROM THE OTHER TYPE list
5995 if ( trk->is_route )
5996 vik_trw_layer_delete_track (vtl, append_track);
5998 vik_trw_layer_delete_route (vtl, append_track);
6001 for (l = append_list; l != NULL; l = g_list_next(l))
6003 g_list_free(append_list);
6004 vik_layer_emit_update( VIK_LAYER(vtl) );
6008 /* merge by segments */
6009 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6011 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6012 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6013 guint segments = vik_track_merge_segments ( trk );
6014 // NB currently no need to redraw as segments not actually shown on the display
6015 // However inform the user of what happened:
6017 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6018 g_snprintf(str, 64, tmp_str, segments);
6019 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6022 /* merge by time routine */
6023 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6025 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6029 GList *tracks_with_timestamp = NULL;
6030 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6031 if (orig_trk->trackpoints &&
6032 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6033 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6038 udata.result = &tracks_with_timestamp;
6039 udata.exclude = orig_trk;
6040 udata.with_timestamps = TRUE;
6041 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6042 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6044 if (!tracks_with_timestamp) {
6045 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6048 g_list_free(tracks_with_timestamp);
6050 static guint threshold_in_minutes = 1;
6051 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6052 _("Merge Threshold..."),
6053 _("Merge when time between tracks less than:"),
6054 &threshold_in_minutes)) {
6058 // keep attempting to merge all tracks until no merges within the time specified is possible
6059 gboolean attempt_merge = TRUE;
6060 GList *nearby_tracks = NULL;
6062 static gpointer params[3];
6064 while ( attempt_merge ) {
6066 // Don't try again unless tracks have changed
6067 attempt_merge = FALSE;
6069 trps = orig_trk->trackpoints;
6073 if (nearby_tracks) {
6074 g_list_free(nearby_tracks);
6075 nearby_tracks = NULL;
6078 params[0] = &nearby_tracks;
6079 params[1] = (gpointer)trps;
6080 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6082 /* get a list of adjacent-in-time tracks */
6083 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6086 GList *l = nearby_tracks;
6088 /* remove trackpoints from merged track, delete track */
6089 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6090 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6092 // Tracks have changed, therefore retry again against all the remaining tracks
6093 attempt_merge = TRUE;
6098 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6101 g_list_free(nearby_tracks);
6103 vik_layer_emit_update( VIK_LAYER(vtl) );
6107 * Split a track at the currently selected trackpoint
6109 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6111 if ( !vtl->current_tpl )
6114 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6115 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6117 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6118 GList *newglist = g_list_alloc ();
6119 newglist->prev = NULL;
6120 newglist->next = vtl->current_tpl->next;
6121 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6122 tr->trackpoints = newglist;
6124 vtl->current_tpl->next->prev = newglist; /* end old track here */
6125 vtl->current_tpl->next = NULL;
6127 // Bounds of the selected track changed due to the split
6128 vik_track_calculate_bounds ( vtl->current_tp_track );
6130 vtl->current_tpl = newglist; /* change tp to first of new track. */
6131 vtl->current_tp_track = tr;
6134 vik_trw_layer_add_route ( vtl, name, tr );
6136 vik_trw_layer_add_track ( vtl, name, tr );
6138 // Bounds of the new track created by the split
6139 vik_track_calculate_bounds ( tr );
6145 // Also need id of newly created track
6148 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6150 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6152 if ( trkf && udata.uuid )
6153 vtl->current_tp_id = udata.uuid;
6155 vtl->current_tp_id = NULL;
6157 vik_layer_emit_update(VIK_LAYER(vtl));
6163 /* split by time routine */
6164 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6166 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6167 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6168 GList *trps = track->trackpoints;
6170 GList *newlists = NULL;
6171 GList *newtps = NULL;
6172 static guint thr = 1;
6179 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6180 _("Split Threshold..."),
6181 _("Split when time between trackpoints exceeds:"),
6186 /* iterate through trackpoints, and copy them into new lists without touching original list */
6187 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6191 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6193 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6196 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6197 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6198 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6200 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6205 if (ts - prev_ts > thr*60) {
6206 /* flush accumulated trackpoints into new list */
6207 newlists = g_list_append(newlists, g_list_reverse(newtps));
6211 /* accumulate trackpoint copies in newtps, in reverse order */
6212 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6214 iter = g_list_next(iter);
6217 newlists = g_list_append(newlists, g_list_reverse(newtps));
6220 /* put lists of trackpoints into tracks */
6222 // Only bother updating if the split results in new tracks
6223 if (g_list_length (newlists) > 1) {
6228 tr = vik_track_copy ( track, FALSE );
6229 tr->trackpoints = (GList *)(iter->data);
6231 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6232 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6233 g_free ( new_tr_name );
6234 vik_track_calculate_bounds ( tr );
6235 iter = g_list_next(iter);
6237 // Remove original track and then update the display
6238 vik_trw_layer_delete_track (vtl, track);
6239 vik_layer_emit_update(VIK_LAYER(vtl));
6241 g_list_free(newlists);
6245 * Split a track by the number of points as specified by the user
6247 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6249 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6251 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6252 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6254 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6259 // Check valid track
6260 GList *trps = track->trackpoints;
6264 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6265 _("Split Every Nth Point"),
6266 _("Split on every Nth point:"),
6267 250, // Default value as per typical limited track capacity of various GPS devices
6271 // Was a valid number returned?
6277 GList *newlists = NULL;
6278 GList *newtps = NULL;
6283 /* accumulate trackpoint copies in newtps, in reverse order */
6284 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6286 if (count >= points) {
6287 /* flush accumulated trackpoints into new list */
6288 newlists = g_list_append(newlists, g_list_reverse(newtps));
6292 iter = g_list_next(iter);
6295 // If there is a remaining chunk put that into the new split list
6296 // This may well be the whole track if no split points were encountered
6298 newlists = g_list_append(newlists, g_list_reverse(newtps));
6301 /* put lists of trackpoints into tracks */
6303 // Only bother updating if the split results in new tracks
6304 if (g_list_length (newlists) > 1) {
6309 tr = vik_track_copy ( track, FALSE );
6310 tr->trackpoints = (GList *)(iter->data);
6312 if ( track->is_route ) {
6313 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6314 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6317 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6318 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6320 g_free ( new_tr_name );
6321 vik_track_calculate_bounds ( tr );
6323 iter = g_list_next(iter);
6325 // Remove original track and then update the display
6326 if ( track->is_route )
6327 vik_trw_layer_delete_route (vtl, track);
6329 vik_trw_layer_delete_track (vtl, track);
6330 vik_layer_emit_update(VIK_LAYER(vtl));
6332 g_list_free(newlists);
6336 * Split a track at the currently selected trackpoint
6338 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6340 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6341 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6342 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6346 * Split a track by its segments
6347 * Routes do not have segments so don't call this for routes
6349 static void trw_layer_split_segments ( menu_array_sublayer values )
6351 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6352 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6359 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6362 for ( i = 0; i < ntracks; i++ ) {
6364 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6365 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6366 g_free ( new_tr_name );
6371 // Remove original track
6372 vik_trw_layer_delete_track ( vtl, trk );
6373 vik_layer_emit_update ( VIK_LAYER(vtl) );
6376 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6379 /* end of split/merge routines */
6381 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6385 // Find available adjacent trackpoint
6386 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6387 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6388 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6390 // Delete current trackpoint
6391 vik_trackpoint_free ( vtl->current_tpl->data );
6392 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6394 // Set to current to the available adjacent trackpoint
6395 vtl->current_tpl = new_tpl;
6397 if ( vtl->current_tp_track ) {
6398 vik_track_calculate_bounds ( vtl->current_tp_track );
6402 // Delete current trackpoint
6403 vik_trackpoint_free ( vtl->current_tpl->data );
6404 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6405 trw_layer_cancel_current_tp ( vtl, FALSE );
6410 * Delete the selected point
6412 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6414 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6416 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6417 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6419 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6424 if ( !vtl->current_tpl )
6427 trw_layer_trackpoint_selected_delete ( vtl, trk );
6429 // Track has been updated so update tps:
6430 trw_layer_cancel_tps_of_track ( vtl, trk );
6432 vik_layer_emit_update ( VIK_LAYER(vtl) );
6436 * Delete adjacent track points at the same position
6437 * AKA Delete Dulplicates on the Properties Window
6439 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6441 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6443 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6444 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6446 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6451 gulong removed = vik_track_remove_dup_points ( trk );
6453 // Track has been updated so update tps:
6454 trw_layer_cancel_tps_of_track ( vtl, trk );
6456 // Inform user how much was deleted as it's not obvious from the normal view
6458 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6459 g_snprintf(str, 64, tmp_str, removed);
6460 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6462 vik_layer_emit_update ( VIK_LAYER(vtl) );
6466 * Delete adjacent track points with the same timestamp
6467 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6469 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6471 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6473 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6474 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6476 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6481 gulong removed = vik_track_remove_same_time_points ( trk );
6483 // Track has been updated so update tps:
6484 trw_layer_cancel_tps_of_track ( vtl, trk );
6486 // Inform user how much was deleted as it's not obvious from the normal view
6488 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6489 g_snprintf(str, 64, tmp_str, removed);
6490 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6492 vik_layer_emit_update ( VIK_LAYER(vtl) );
6498 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6500 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6502 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6503 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6505 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6510 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6512 vik_layer_emit_update ( VIK_LAYER(vtl) );
6515 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6517 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6519 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6520 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6522 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6527 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6529 vik_layer_emit_update ( VIK_LAYER(vtl) );
6535 static void trw_layer_reverse ( menu_array_sublayer values )
6537 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6539 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6540 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6542 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6547 vik_track_reverse ( track );
6549 vik_layer_emit_update ( VIK_LAYER(vtl) );
6553 * Open a diary at the specified date
6555 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6558 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6559 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6560 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6561 g_error_free ( err );
6567 * Open a diary at the date of the track or waypoint
6569 static void trw_layer_diary ( menu_array_sublayer values )
6571 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6573 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6574 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6580 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6581 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6582 trw_layer_diary_open ( vtl, date_buf );
6585 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6587 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6588 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6594 if ( wpt->has_timestamp ) {
6595 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6596 trw_layer_diary_open ( vtl, date_buf );
6599 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6604 * Similar to trw_layer_enum_item, but this uses a sorted method
6607 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6609 GList **list = (GList**)udata;
6610 // *list = g_list_prepend(*all, key); //unsorted method
6611 // Sort named list alphabetically
6612 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6617 * Now Waypoint specific sort
6619 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6621 GList **list = (GList**)udata;
6622 // Sort named list alphabetically
6623 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6627 * Track specific sort
6629 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6631 GList **list = (GList**)udata;
6632 // Sort named list alphabetically
6633 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6638 gboolean has_same_track_name;
6639 const gchar *same_track_name;
6640 } same_track_name_udata;
6642 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6644 const gchar* namea = (const gchar*) aa;
6645 const gchar* nameb = (const gchar*) bb;
6648 gint result = strcmp ( namea, nameb );
6650 if ( result == 0 ) {
6651 // Found two names the same
6652 same_track_name_udata *user_data = udata;
6653 user_data->has_same_track_name = TRUE;
6654 user_data->same_track_name = namea;
6657 // Leave ordering the same
6662 * Find out if any tracks have the same name in this hash table
6664 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6666 // Sort items by name, then compare if any next to each other are the same
6668 GList *track_names = NULL;
6669 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6672 if ( ! track_names )
6675 same_track_name_udata udata;
6676 udata.has_same_track_name = FALSE;
6678 // Use sort routine to traverse list comparing items
6679 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6680 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6681 // Still no tracks...
6685 return udata.has_same_track_name;
6689 * Force unqiue track names for the track table specified
6690 * Note the panel is a required parameter to enable the update of the names displayed
6691 * Specify if on tracks or else on routes
6693 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6695 // . Search list for an instance of repeated name
6696 // . get track of this name
6697 // . create new name
6698 // . rename track & update equiv. treeview iter
6699 // . repeat until all different
6701 same_track_name_udata udata;
6703 GList *track_names = NULL;
6704 udata.has_same_track_name = FALSE;
6705 udata.same_track_name = NULL;
6707 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6710 if ( ! track_names )
6713 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6715 // Still no tracks...
6716 if ( ! dummy_list1 )
6719 while ( udata.has_same_track_name ) {
6721 // Find a track with the same name
6724 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6726 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6730 g_critical("Houston, we've had a problem.");
6731 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6732 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6737 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6738 vik_track_set_name ( trk, newname );
6744 // Need want key of it for treeview update
6745 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6747 if ( trkf && udataU.uuid ) {
6751 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6753 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6756 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6758 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6760 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6764 // Start trying to find same names again...
6766 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6767 udata.has_same_track_name = FALSE;
6768 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6770 // No tracks any more - give up searching
6771 if ( ! dummy_list2 )
6772 udata.has_same_track_name = FALSE;
6776 vik_layers_panel_emit_update ( vlp );
6779 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6781 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6784 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6785 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6786 iter = &(vtl->tracks_iter);
6787 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6789 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6790 iter = &(vtl->routes_iter);
6791 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6793 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6794 iter = &(vtl->waypoints_iter);
6795 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6799 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6802 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6804 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6807 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6808 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6809 iter = &(vtl->tracks_iter);
6810 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6812 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6813 iter = &(vtl->routes_iter);
6814 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6816 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6817 iter = &(vtl->waypoints_iter);
6818 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6822 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6828 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6830 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6833 // Ensure list of track names offered is unique
6834 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6835 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6836 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6837 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6843 // Sort list alphabetically for better presentation
6844 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6847 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6851 // Get list of items to delete from the user
6852 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6855 _("Delete Selection"),
6856 _("Select tracks to delete"));
6859 // Delete requested tracks
6860 // since specificly requested, IMHO no need for extra confirmation
6861 if ( delete_list ) {
6863 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6864 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6865 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6867 g_list_free(delete_list);
6868 vik_layer_emit_update( VIK_LAYER(vtl) );
6875 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6877 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6880 // Ensure list of track names offered is unique
6881 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6882 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6883 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6884 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6890 // Sort list alphabetically for better presentation
6891 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6894 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6898 // Get list of items to delete from the user
6899 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6902 _("Delete Selection"),
6903 _("Select routes to delete") );
6906 // Delete requested routes
6907 // since specificly requested, IMHO no need for extra confirmation
6908 if ( delete_list ) {
6910 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6911 // This deletes first route it finds of that name (but uniqueness is enforced above)
6912 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6914 g_list_free(delete_list);
6915 vik_layer_emit_update( VIK_LAYER(vtl) );
6920 gboolean has_same_waypoint_name;
6921 const gchar *same_waypoint_name;
6922 } same_waypoint_name_udata;
6924 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6926 const gchar* namea = (const gchar*) aa;
6927 const gchar* nameb = (const gchar*) bb;
6930 gint result = strcmp ( namea, nameb );
6932 if ( result == 0 ) {
6933 // Found two names the same
6934 same_waypoint_name_udata *user_data = udata;
6935 user_data->has_same_waypoint_name = TRUE;
6936 user_data->same_waypoint_name = namea;
6939 // Leave ordering the same
6944 * Find out if any waypoints have the same name in this layer
6946 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6948 // Sort items by name, then compare if any next to each other are the same
6950 GList *waypoint_names = NULL;
6951 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6954 if ( ! waypoint_names )
6957 same_waypoint_name_udata udata;
6958 udata.has_same_waypoint_name = FALSE;
6960 // Use sort routine to traverse list comparing items
6961 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6962 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6963 // Still no waypoints...
6967 return udata.has_same_waypoint_name;
6971 * Force unqiue waypoint names for this layer
6972 * Note the panel is a required parameter to enable the update of the names displayed
6974 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6976 // . Search list for an instance of repeated name
6977 // . get waypoint of this name
6978 // . create new name
6979 // . rename waypoint & update equiv. treeview iter
6980 // . repeat until all different
6982 same_waypoint_name_udata udata;
6984 GList *waypoint_names = NULL;
6985 udata.has_same_waypoint_name = FALSE;
6986 udata.same_waypoint_name = NULL;
6988 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6991 if ( ! waypoint_names )
6994 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6996 // Still no waypoints...
6997 if ( ! dummy_list1 )
7000 while ( udata.has_same_waypoint_name ) {
7002 // Find a waypoint with the same name
7003 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7007 g_critical("Houston, we've had a problem.");
7008 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7009 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7014 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7016 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7018 // Start trying to find same names again...
7019 waypoint_names = NULL;
7020 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7021 udata.has_same_waypoint_name = FALSE;
7022 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7024 // No waypoints any more - give up searching
7025 if ( ! dummy_list2 )
7026 udata.has_same_waypoint_name = FALSE;
7030 vik_layers_panel_emit_update ( vlp );
7036 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7038 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7041 // Ensure list of waypoint names offered is unique
7042 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7043 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7044 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7045 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7051 // Sort list alphabetically for better presentation
7052 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7054 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7058 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7060 // Get list of items to delete from the user
7061 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7064 _("Delete Selection"),
7065 _("Select waypoints to delete"));
7068 // Delete requested waypoints
7069 // since specificly requested, IMHO no need for extra confirmation
7070 if ( delete_list ) {
7072 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7073 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7074 trw_layer_delete_waypoint_by_name (vtl, l->data);
7076 g_list_free(delete_list);
7078 trw_layer_calculate_bounds_waypoints ( vtl );
7079 vik_layer_emit_update( VIK_LAYER(vtl) );
7087 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7089 vik_treeview_item_toggle_visible ( vt, it );
7095 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7097 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7103 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7105 wp->visible = GPOINTER_TO_INT (on_off);
7111 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7113 wp->visible = !wp->visible;
7119 static void trw_layer_waypoints_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->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7124 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7126 vik_layer_emit_update ( VIK_LAYER(vtl) );
7132 static void trw_layer_waypoints_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->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7137 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7139 vik_layer_emit_update ( VIK_LAYER(vtl) );
7145 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7147 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7148 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7149 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7151 vik_layer_emit_update ( VIK_LAYER(vtl) );
7157 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7159 trk->visible = GPOINTER_TO_INT (on_off);
7165 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7167 trk->visible = !trk->visible;
7173 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7175 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7176 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7177 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7178 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7180 vik_layer_emit_update ( VIK_LAYER(vtl) );
7186 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7188 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7189 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7190 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7191 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7193 vik_layer_emit_update ( VIK_LAYER(vtl) );
7199 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7201 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7202 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7203 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7205 vik_layer_emit_update ( VIK_LAYER(vtl) );
7211 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7213 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7214 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7215 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7216 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7218 vik_layer_emit_update ( VIK_LAYER(vtl) );
7224 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7226 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7227 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7228 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7229 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7231 vik_layer_emit_update ( VIK_LAYER(vtl) );
7237 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7239 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7240 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7241 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7243 vik_layer_emit_update ( VIK_LAYER(vtl) );
7247 * vik_trw_layer_build_waypoint_list_t:
7249 * Helper function to construct a list of #vik_trw_waypoint_list_t
7251 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7253 GList *waypoints_and_layers = NULL;
7254 // build waypoints_and_layers list
7255 while ( waypoints ) {
7256 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7257 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7259 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7260 waypoints = g_list_next ( waypoints );
7262 return waypoints_and_layers;
7266 * trw_layer_create_waypoint_list:
7268 * Create the latest list of waypoints with the associated layer(s)
7269 * Although this will always be from a single layer here
7271 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7273 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7274 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7276 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7280 * trw_layer_analyse_close:
7282 * Stuff to do on dialog closure
7284 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7286 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7287 gtk_widget_destroy ( dialog );
7288 vtl->tracks_analysis_dialog = NULL;
7292 * vik_trw_layer_build_track_list_t:
7294 * Helper function to construct a list of #vik_trw_track_list_t
7296 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7298 GList *tracks_and_layers = NULL;
7299 // build tracks_and_layers list
7301 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7302 vtdl->trk = VIK_TRACK(tracks->data);
7304 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7305 tracks = g_list_next ( tracks );
7307 return tracks_and_layers;
7311 * trw_layer_create_track_list:
7313 * Create the latest list of tracks with the associated layer(s)
7314 * Although this will always be from a single layer here
7316 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7318 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7319 GList *tracks = NULL;
7320 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7321 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7323 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7325 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7328 static void trw_layer_tracks_stats ( menu_array_layer values )
7330 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7331 // There can only be one!
7332 if ( vtl->tracks_analysis_dialog )
7335 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7336 VIK_LAYER(vtl)->name,
7338 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7339 trw_layer_create_track_list,
7340 trw_layer_analyse_close );
7346 static void trw_layer_routes_stats ( menu_array_layer values )
7348 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7349 // There can only be one!
7350 if ( vtl->tracks_analysis_dialog )
7353 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7354 VIK_LAYER(vtl)->name,
7356 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7357 trw_layer_create_track_list,
7358 trw_layer_analyse_close );
7361 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7363 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7364 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7366 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7369 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7371 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7372 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7375 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7376 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7380 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7382 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7383 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7386 if ( !strncmp(wp->comment, "http", 4) ) {
7387 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7388 } else if ( !strncmp(wp->description, "http", 4) ) {
7389 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7393 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7395 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7397 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7399 // No actual change to the name supplied
7401 if (strcmp(newname, wp->name) == 0 )
7404 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7407 // An existing waypoint has been found with the requested name
7408 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7409 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7414 // Update WP name and refresh the treeview
7415 vik_waypoint_set_name (wp, newname);
7417 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7418 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7420 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7425 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7427 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7429 // No actual change to the name supplied
7431 if (strcmp(newname, trk->name) == 0)
7434 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7437 // An existing track has been found with the requested name
7438 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7439 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7443 // Update track name and refresh GUI parts
7444 vik_track_set_name (trk, newname);
7446 // Update any subwindows that could be displaying this track which has changed name
7447 // Only one Track Edit Window
7448 if ( l->current_tp_track == trk && l->tpwin ) {
7449 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7451 // Property Dialog of the track
7452 vik_trw_layer_propwin_update ( trk );
7454 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7455 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7457 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7462 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7464 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7466 // No actual change to the name supplied
7468 if (strcmp(newname, trk->name) == 0)
7471 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7474 // An existing track has been found with the requested name
7475 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7476 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7480 // Update track name and refresh GUI parts
7481 vik_track_set_name (trk, newname);
7483 // Update any subwindows that could be displaying this track which has changed name
7484 // Only one Track Edit Window
7485 if ( l->current_tp_track == trk && l->tpwin ) {
7486 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7488 // Property Dialog of the track
7489 vik_trw_layer_propwin_update ( trk );
7491 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7492 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7494 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7501 static gboolean is_valid_geocache_name ( gchar *str )
7503 gint len = strlen ( str );
7504 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]));
7507 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7509 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7510 a_acquire_set_filter_track ( trk );
7513 #ifdef VIK_CONFIG_GOOGLE
7514 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7516 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7517 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7520 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7522 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7524 gchar *escaped = uri_escape ( tr->comment );
7525 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7526 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7533 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7534 /* viewpoint is now available instead */
7535 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7537 static menu_array_sublayer pass_along;
7539 gboolean rv = FALSE;
7541 pass_along[MA_VTL] = l;
7542 pass_along[MA_VLP] = vlp;
7543 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7544 pass_along[MA_SUBLAYER_ID] = sublayer;
7545 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7546 pass_along[MA_VVP] = vvp;
7547 pass_along[MA_TV_ITER] = iter;
7548 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7550 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7554 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7556 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7557 gtk_widget_show ( item );
7559 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7560 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7561 if (tr && tr->property_dialog)
7562 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7564 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7565 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7566 if (tr && tr->property_dialog)
7567 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7570 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7573 gtk_widget_show ( item );
7575 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7577 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7578 gtk_widget_show ( item );
7580 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7581 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7582 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7583 gtk_widget_show ( item );
7585 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7587 // Always create separator as now there is always at least the transform menu option
7588 item = gtk_menu_item_new ();
7589 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7590 gtk_widget_show ( item );
7592 /* could be a right-click using the tool */
7593 if ( vlp != NULL ) {
7594 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7595 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7596 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7597 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7598 gtk_widget_show ( item );
7601 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7603 if ( wp && wp->name ) {
7604 if ( is_valid_geocache_name ( wp->name ) ) {
7605 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7606 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7607 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7608 gtk_widget_show ( item );
7610 #ifdef VIK_CONFIG_GEOTAG
7611 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7613 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7614 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7615 gtk_widget_show ( item );
7619 if ( wp && wp->image )
7621 // Set up image paramater
7622 pass_along[MA_MISC] = wp->image;
7624 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7625 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
7626 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7627 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7628 gtk_widget_show ( item );
7630 #ifdef VIK_CONFIG_GEOTAG
7631 GtkWidget *geotag_submenu = gtk_menu_new ();
7632 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7633 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7634 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7635 gtk_widget_show ( item );
7636 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7638 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7640 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7641 gtk_widget_show ( item );
7643 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7645 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7646 gtk_widget_show ( item );
7652 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7653 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7654 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7655 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7657 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7658 gtk_widget_show ( item );
7664 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7665 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7667 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7668 gtk_widget_show ( item );
7669 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7670 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7671 gtk_widget_set_sensitive ( item, TRUE );
7673 gtk_widget_set_sensitive ( item, FALSE );
7676 item = gtk_menu_item_new ();
7677 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7678 gtk_widget_show ( item );
7681 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7684 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7685 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7686 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7687 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7688 gtk_widget_show ( item );
7691 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7693 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7694 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7695 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7696 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7697 gtk_widget_show ( item );
7699 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7700 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7701 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7702 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7703 gtk_widget_show ( item );
7705 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7706 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7707 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7708 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7709 gtk_widget_show ( item );
7711 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7712 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7713 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7714 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7715 gtk_widget_show ( item );
7717 GtkWidget *vis_submenu = gtk_menu_new ();
7718 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720 gtk_widget_show ( item );
7721 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7727 gtk_widget_show ( item );
7729 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7730 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7731 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7732 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7733 gtk_widget_show ( item );
7735 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7736 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7739 gtk_widget_show ( item );
7741 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7742 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7743 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7744 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7747 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7751 if ( l->current_track && !l->current_track->is_route ) {
7752 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7754 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7755 gtk_widget_show ( item );
7757 item = gtk_menu_item_new ();
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7759 gtk_widget_show ( item );
7762 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7763 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7764 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7765 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7766 gtk_widget_show ( item );
7768 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7769 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7771 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7772 gtk_widget_show ( item );
7773 // Make it available only when a new track *not* already in progress
7774 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7776 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7777 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7779 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7780 gtk_widget_show ( item );
7782 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7783 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7784 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7785 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7786 gtk_widget_show ( item );
7788 GtkWidget *vis_submenu = gtk_menu_new ();
7789 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7790 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7791 gtk_widget_show ( item );
7792 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7794 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7797 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7798 gtk_widget_show ( item );
7800 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7803 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7804 gtk_widget_show ( item );
7806 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7807 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7809 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7811 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7812 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7814 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7815 gtk_widget_show ( item );
7817 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7820 gtk_widget_show ( item );
7823 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7827 if ( l->current_track && l->current_track->is_route ) {
7828 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7829 // Reuse finish track method
7830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7831 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7832 gtk_widget_show ( item );
7834 item = gtk_menu_item_new ();
7835 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7836 gtk_widget_show ( item );
7839 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7840 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7842 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7843 gtk_widget_show ( item );
7845 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7846 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7848 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7849 gtk_widget_show ( item );
7850 // Make it available only when a new track *not* already in progress
7851 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7854 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7855 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7856 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7857 gtk_widget_show ( item );
7859 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7860 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7861 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7862 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7863 gtk_widget_show ( item );
7865 GtkWidget *vis_submenu = gtk_menu_new ();
7866 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7867 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7868 gtk_widget_show ( item );
7869 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7871 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7872 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7874 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7875 gtk_widget_show ( item );
7877 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7878 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7881 gtk_widget_show ( item );
7883 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7884 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7885 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7886 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7888 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7889 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7890 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7891 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7893 gtk_widget_show ( item );
7895 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7897 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7898 gtk_widget_show ( item );
7902 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7903 GtkWidget *submenu_sort = gtk_menu_new ();
7904 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7905 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7906 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7907 gtk_widget_show ( item );
7908 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7910 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7911 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7912 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7913 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7914 gtk_widget_show ( item );
7916 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7917 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7919 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7920 gtk_widget_show ( item );
7923 GtkWidget *upload_submenu = gtk_menu_new ();
7925 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7927 item = gtk_menu_item_new ();
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7929 gtk_widget_show ( item );
7931 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7932 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7933 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7934 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7935 if ( l->current_track ) {
7936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7937 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7938 gtk_widget_show ( item );
7941 item = gtk_menu_item_new ();
7942 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7943 gtk_widget_show ( item );
7946 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7947 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7949 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7950 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7952 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7953 gtk_widget_show ( item );
7955 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7957 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7958 gtk_widget_show ( item );
7960 GtkWidget *goto_submenu;
7961 goto_submenu = gtk_menu_new ();
7962 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7963 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7964 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7965 gtk_widget_show ( item );
7966 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7968 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7969 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7970 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7971 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7972 gtk_widget_show ( item );
7974 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7977 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7978 gtk_widget_show ( item );
7980 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7981 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7982 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7983 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7984 gtk_widget_show ( item );
7986 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7987 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7989 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7990 gtk_widget_show ( item );
7992 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7993 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7995 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7996 gtk_widget_show ( item );
7998 // Routes don't have speeds
7999 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8000 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8003 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8004 gtk_widget_show ( item );
8007 GtkWidget *combine_submenu;
8008 combine_submenu = gtk_menu_new ();
8009 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8010 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8011 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8012 gtk_widget_show ( item );
8013 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8015 // Routes don't have times or segments...
8016 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8017 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8019 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8020 gtk_widget_show ( item );
8022 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8024 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8025 gtk_widget_show ( item );
8028 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8030 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8031 gtk_widget_show ( item );
8033 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8034 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8036 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8038 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8039 gtk_widget_show ( item );
8041 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8042 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8044 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8046 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8047 gtk_widget_show ( item );
8049 GtkWidget *split_submenu;
8050 split_submenu = gtk_menu_new ();
8051 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8053 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8054 gtk_widget_show ( item );
8055 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8057 // Routes don't have times or segments...
8058 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8059 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8061 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8062 gtk_widget_show ( item );
8064 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8065 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8067 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8068 gtk_widget_show ( item );
8071 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8073 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8074 gtk_widget_show ( item );
8076 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8078 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8079 gtk_widget_show ( item );
8080 // Make it available only when a trackpoint is selected.
8081 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8083 GtkWidget *insert_submenu = gtk_menu_new ();
8084 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8086 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8087 gtk_widget_show ( item );
8088 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8090 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8092 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8093 gtk_widget_show ( item );
8094 // Make it available only when a point is selected
8095 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8097 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8099 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8100 gtk_widget_show ( item );
8101 // Make it available only when a point is selected
8102 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8104 GtkWidget *delete_submenu;
8105 delete_submenu = gtk_menu_new ();
8106 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8107 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8108 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8109 gtk_widget_show ( item );
8110 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8112 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8113 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8115 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8116 gtk_widget_show ( item );
8117 // Make it available only when a point is selected
8118 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8120 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8122 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8123 gtk_widget_show ( item );
8125 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8127 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8128 gtk_widget_show ( item );
8130 GtkWidget *transform_submenu;
8131 transform_submenu = gtk_menu_new ();
8132 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8133 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8134 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8135 gtk_widget_show ( item );
8136 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8138 GtkWidget *dem_submenu;
8139 dem_submenu = gtk_menu_new ();
8140 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8141 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
8142 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8143 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8145 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8147 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8148 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8149 gtk_widget_show ( item );
8151 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8153 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8154 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8155 gtk_widget_show ( item );
8157 GtkWidget *smooth_submenu;
8158 smooth_submenu = gtk_menu_new ();
8159 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8160 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8161 gtk_widget_show ( item );
8162 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8164 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8166 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8167 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8168 gtk_widget_show ( item );
8170 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8172 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8173 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8174 gtk_widget_show ( item );
8176 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8177 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8179 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8180 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8181 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8182 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8183 gtk_widget_show ( item );
8185 // Routes don't have timestamps - so this is only available for tracks
8186 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8187 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8189 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8190 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8191 gtk_widget_show ( item );
8194 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8195 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8197 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8198 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8200 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8201 gtk_widget_show ( item );
8203 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8204 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8207 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8208 gtk_widget_show ( item );
8211 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8213 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8214 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8216 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8217 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
8218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8219 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8220 gtk_widget_show ( item );
8223 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8224 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8226 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8227 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8228 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8229 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8230 gtk_widget_show ( item );
8232 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8233 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8235 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8236 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8237 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8238 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8239 gtk_widget_show ( item );
8241 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8242 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8243 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
8244 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8245 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8246 gtk_widget_show ( item );
8249 // ATM can't upload a single waypoint but can do waypoints to a GPS
8250 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8251 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8253 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8254 gtk_widget_show ( item );
8255 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8258 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8260 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8261 gtk_widget_show ( item );
8265 // Only made available if a suitable program is installed
8266 if ( have_diary_program ) {
8267 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8268 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8269 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8271 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8272 gtk_widget_show ( item );
8276 #ifdef VIK_CONFIG_GOOGLE
8277 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8279 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8280 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8281 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8282 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8283 gtk_widget_show ( item );
8287 // Some things aren't usable with routes
8288 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8289 #ifdef VIK_CONFIG_OPENSTREETMAP
8290 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8291 // Convert internal pointer into track
8292 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8293 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8294 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8295 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8296 gtk_widget_show ( item );
8299 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8300 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8301 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8302 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8303 gtk_widget_show ( item );
8305 /* ATM This function is only available via the layers panel, due to needing a vlp */
8307 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8308 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8309 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8311 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8312 gtk_widget_show ( item );
8316 #ifdef VIK_CONFIG_GEOTAG
8317 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8318 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8319 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8320 gtk_widget_show ( item );
8324 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8325 // Only show on viewport popmenu when a trackpoint is selected
8326 if ( ! vlp && l->current_tpl ) {
8328 item = gtk_menu_item_new ();
8329 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8330 gtk_widget_show ( item );
8332 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8336 gtk_widget_show ( item );
8340 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8341 GtkWidget *transform_submenu;
8342 transform_submenu = gtk_menu_new ();
8343 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8344 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8345 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8346 gtk_widget_show ( item );
8347 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8349 GtkWidget *dem_submenu;
8350 dem_submenu = gtk_menu_new ();
8351 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8352 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
8353 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8354 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8356 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8358 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8359 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8360 gtk_widget_show ( item );
8362 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8363 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8364 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8365 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8366 gtk_widget_show ( item );
8369 gtk_widget_show_all ( GTK_WIDGET(menu) );
8374 // TODO: Probably better to rework this track manipulation in viktrack.c
8375 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8378 if (!vtl->current_tpl)
8381 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8382 VikTrackpoint *tp_other = NULL;
8385 if (!vtl->current_tpl->prev)
8387 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8389 if (!vtl->current_tpl->next)
8391 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8394 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8397 VikTrackpoint *tp_new = vik_trackpoint_new();
8398 struct LatLon ll_current, ll_other;
8399 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8400 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8402 /* main positional interpolation */
8403 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8404 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8406 /* Now other properties that can be interpolated */
8407 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8409 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8410 /* Note here the division is applied to each part, then added
8411 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8412 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8413 tp_new->has_timestamp = TRUE;
8416 if (tp_current->speed != NAN && tp_other->speed != NAN)
8417 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8419 /* TODO - improve interpolation of course, as it may not be correct.
8420 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8421 [similar applies if value is in radians] */
8422 if (tp_current->course != NAN && tp_other->course != NAN)
8423 tp_new->course = (tp_current->course + tp_other->course)/2;
8425 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8427 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8428 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8430 // Otherwise try routes
8431 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8435 gint index = g_list_index ( trk->trackpoints, tp_current );
8439 // NB no recalculation of bounds since it is inserted between points
8440 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8445 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8451 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8455 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8457 if ( vtl->current_tpl )
8459 vtl->current_tpl = NULL;
8460 vtl->current_tp_track = NULL;
8461 vtl->current_tp_id = NULL;
8462 vik_layer_emit_update(VIK_LAYER(vtl));
8466 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8468 g_assert ( vtl->tpwin != NULL );
8469 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8470 trw_layer_cancel_current_tp ( vtl, TRUE );
8472 if ( vtl->current_tpl == NULL )
8475 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8477 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8478 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8480 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8482 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8484 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8488 trw_layer_trackpoint_selected_delete ( vtl, tr );
8490 if ( vtl->current_tpl )
8491 // Reset dialog with the available adjacent trackpoint
8492 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8494 vik_layer_emit_update(VIK_LAYER(vtl));
8496 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8498 if ( vtl->current_tp_track )
8499 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8500 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8502 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8504 if ( vtl->current_tp_track )
8505 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8506 vik_layer_emit_update(VIK_LAYER(vtl));
8508 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8510 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8511 vik_layer_emit_update(VIK_LAYER(vtl));
8513 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8514 vik_layer_emit_update(VIK_LAYER(vtl));
8518 * trw_layer_dialog_shift:
8519 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8521 * Try to reposition a dialog if it's over the specified coord
8522 * so to not obscure the item of interest
8524 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8526 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8528 // Attempt force dialog to be shown so we can find out where it is more reliably...
8529 while ( gtk_events_pending() )
8530 gtk_main_iteration ();
8532 // get parent window position & size
8533 gint win_pos_x, win_pos_y;
8534 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8536 gint win_size_x, win_size_y;
8537 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8539 // get own dialog size
8540 gint dia_size_x, dia_size_y;
8541 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8543 // get own dialog position
8544 gint dia_pos_x, dia_pos_y;
8545 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8547 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8548 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8550 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8552 gint vp_xx, vp_yy; // In viewport pixels
8553 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8555 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8559 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8561 // Transform Viewport pixels into absolute pixels
8562 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8563 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8565 // Is dialog over the point (to within an ^^ edge value)
8566 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8567 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8571 gint hh = vik_viewport_get_height ( vvp );
8573 // Consider the difference in viewport to the full window
8574 gint offset_y = dest_y;
8575 // Add difference between dialog and window sizes
8576 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8578 if ( vp_yy > hh/2 ) {
8579 // Point in bottom half, move window to top half
8580 gtk_window_move ( dialog, dia_pos_x, offset_y );
8583 // Point in top half, move dialog down
8584 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8588 // Shift left<->right
8589 gint ww = vik_viewport_get_width ( vvp );
8591 // Consider the difference in viewport to the full window
8592 gint offset_x = dest_x;
8593 // Add difference between dialog and window sizes
8594 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8596 if ( vp_xx > ww/2 ) {
8597 // Point on right, move window to left
8598 gtk_window_move ( dialog, offset_x, dia_pos_y );
8601 // Point on left, move right
8602 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8610 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8614 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8615 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8616 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8617 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8619 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8621 if ( vtl->current_tpl ) {
8622 // get tp pixel position
8623 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8625 // Shift up<->down to try not to obscure the trackpoint.
8626 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8630 if ( vtl->current_tpl )
8631 if ( vtl->current_tp_track )
8632 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8633 /* set layer name and TP data */
8636 /***************************************************************************
8638 ***************************************************************************/
8640 /*** Utility data structures and functions ****/
8644 gint closest_x, closest_y;
8645 gboolean draw_images;
8646 gpointer *closest_wp_id;
8647 VikWaypoint *closest_wp;
8653 gint closest_x, closest_y;
8654 gpointer closest_track_id;
8655 VikTrackpoint *closest_tp;
8661 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8667 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8669 // If waypoint has an image then use the image size to select
8670 if ( params->draw_images && wp->image ) {
8671 gint slackx, slacky;
8672 slackx = wp->image_width / 2;
8673 slacky = wp->image_height / 2;
8675 if ( x <= params->x + slackx && x >= params->x - slackx
8676 && y <= params->y + slacky && y >= params->y - slacky ) {
8677 params->closest_wp_id = id;
8678 params->closest_wp = wp;
8679 params->closest_x = x;
8680 params->closest_y = y;
8683 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8684 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8685 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8687 params->closest_wp_id = id;
8688 params->closest_wp = wp;
8689 params->closest_x = x;
8690 params->closest_y = y;
8694 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8696 GList *tpl = t->trackpoints;
8702 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8708 tp = VIK_TRACKPOINT(tpl->data);
8710 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8712 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8713 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8714 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8716 params->closest_track_id = id;
8717 params->closest_tp = tp;
8718 params->closest_tpl = tpl;
8719 params->closest_x = x;
8720 params->closest_y = y;
8726 // ATM: Leave this as 'Track' only.
8727 // Not overly bothered about having a snap to route trackpoint capability
8728 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8730 TPSearchParams params;
8734 params.closest_track_id = NULL;
8735 params.closest_tp = NULL;
8736 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8737 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8738 return params.closest_tp;
8741 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8743 WPSearchParams params;
8747 params.draw_images = vtl->drawimages;
8748 params.closest_wp = NULL;
8749 params.closest_wp_id = NULL;
8750 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8751 return params.closest_wp;
8755 // Some forward declarations
8756 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8757 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8758 static void marker_end_move ( tool_ed_t *t );
8761 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8765 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8767 // Here always allow snapping back to the original location
8768 // this is useful when one decides not to move the thing afterall
8769 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8772 if ( event->state & GDK_CONTROL_MASK )
8774 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8776 new_coord = tp->coord;
8780 if ( event->state & GDK_SHIFT_MASK )
8782 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8784 new_coord = wp->coord;
8788 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8790 marker_moveto ( t, x, y );
8797 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8799 if ( t->holding && event->button == 1 )
8802 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8805 if ( event->state & GDK_CONTROL_MASK )
8807 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8809 new_coord = tp->coord;
8813 if ( event->state & GDK_SHIFT_MASK )
8815 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8817 new_coord = wp->coord;
8820 marker_end_move ( t );
8822 // Determine if working on a waypoint or a trackpoint
8823 if ( t->is_waypoint ) {
8824 // Update waypoint position
8825 vtl->current_wp->coord = new_coord;
8826 trw_layer_calculate_bounds_waypoints ( vtl );
8827 // Reset waypoint pointer
8828 vtl->current_wp = NULL;
8829 vtl->current_wp_id = NULL;
8832 if ( vtl->current_tpl ) {
8833 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8835 if ( vtl->current_tp_track )
8836 vik_track_calculate_bounds ( vtl->current_tp_track );
8839 if ( vtl->current_tp_track )
8840 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8841 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8845 vik_layer_emit_update ( VIK_LAYER(vtl) );
8852 Returns true if a waypoint or track is found near the requested event position for this particular layer
8853 The item found is automatically selected
8854 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8856 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8858 if ( event->button != 1 )
8861 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8864 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8868 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8870 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8872 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8873 WPSearchParams wp_params;
8874 wp_params.vvp = vvp;
8875 wp_params.x = event->x;
8876 wp_params.y = event->y;
8877 wp_params.draw_images = vtl->drawimages;
8878 wp_params.closest_wp_id = NULL;
8879 wp_params.closest_wp = NULL;
8881 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8883 if ( wp_params.closest_wp ) {
8886 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8888 // Too easy to move it so must be holding shift to start immediately moving it
8889 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8890 if ( event->state & GDK_SHIFT_MASK ||
8891 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8892 // Put into 'move buffer'
8893 // NB vvp & vw already set in tet
8894 tet->vtl = (gpointer)vtl;
8895 tet->is_waypoint = TRUE;
8897 marker_begin_move (tet, event->x, event->y);
8900 vtl->current_wp = wp_params.closest_wp;
8901 vtl->current_wp_id = wp_params.closest_wp_id;
8903 if ( event->type == GDK_2BUTTON_PRESS ) {
8904 if ( vtl->current_wp->image ) {
8905 menu_array_sublayer values;
8906 values[MA_VTL] = vtl;
8907 values[MA_MISC] = vtl->current_wp->image;
8908 trw_layer_show_picture ( values );
8912 vik_layer_emit_update ( VIK_LAYER(vtl) );
8918 // Used for both track and route lists
8919 TPSearchParams tp_params;
8920 tp_params.vvp = vvp;
8921 tp_params.x = event->x;
8922 tp_params.y = event->y;
8923 tp_params.closest_track_id = NULL;
8924 tp_params.closest_tp = NULL;
8925 tp_params.closest_tpl = NULL;
8926 tp_params.bbox = bbox;
8928 if (vtl->tracks_visible) {
8929 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8931 if ( tp_params.closest_tp ) {
8933 // Always select + highlight the track
8934 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8936 tet->is_waypoint = FALSE;
8938 // Select the Trackpoint
8939 // Can move it immediately when control held or it's the previously selected tp
8940 if ( event->state & GDK_CONTROL_MASK ||
8941 vtl->current_tpl == tp_params.closest_tpl ) {
8942 // Put into 'move buffer'
8943 // NB vvp & vw already set in tet
8944 tet->vtl = (gpointer)vtl;
8945 marker_begin_move (tet, event->x, event->y);
8948 vtl->current_tpl = tp_params.closest_tpl;
8949 vtl->current_tp_id = tp_params.closest_track_id;
8950 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8952 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8955 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8957 vik_layer_emit_update ( VIK_LAYER(vtl) );
8962 // Try again for routes
8963 if (vtl->routes_visible) {
8964 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8966 if ( tp_params.closest_tp ) {
8968 // Always select + highlight the track
8969 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8971 tet->is_waypoint = FALSE;
8973 // Select the Trackpoint
8974 // Can move it immediately when control held or it's the previously selected tp
8975 if ( event->state & GDK_CONTROL_MASK ||
8976 vtl->current_tpl == tp_params.closest_tpl ) {
8977 // Put into 'move buffer'
8978 // NB vvp & vw already set in tet
8979 tet->vtl = (gpointer)vtl;
8980 marker_begin_move (tet, event->x, event->y);
8983 vtl->current_tpl = tp_params.closest_tpl;
8984 vtl->current_tp_id = tp_params.closest_track_id;
8985 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8987 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8990 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8992 vik_layer_emit_update ( VIK_LAYER(vtl) );
8997 /* these aren't the droids you're looking for */
8998 vtl->current_wp = NULL;
8999 vtl->current_wp_id = NULL;
9000 trw_layer_cancel_current_tp ( vtl, FALSE );
9003 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9008 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9010 if ( event->button != 3 )
9013 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9016 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9019 /* Post menu for the currently selected item */
9021 /* See if a track is selected */
9022 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9023 if ( track && track->visible ) {
9025 if ( track->name ) {
9027 if ( vtl->track_right_click_menu )
9028 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9030 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9037 if ( track->is_route )
9038 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9040 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9042 if ( trkf && udataU.uuid ) {
9045 if ( track->is_route )
9046 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9048 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9050 trw_layer_sublayer_add_menu_items ( vtl,
9051 vtl->track_right_click_menu,
9053 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9059 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9065 /* See if a waypoint is selected */
9066 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9067 if ( waypoint && waypoint->visible ) {
9068 if ( waypoint->name ) {
9070 if ( vtl->wp_right_click_menu )
9071 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9073 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9076 udata.wp = waypoint;
9079 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9081 if ( wpf && udata.uuid ) {
9082 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9084 trw_layer_sublayer_add_menu_items ( vtl,
9085 vtl->wp_right_click_menu,
9087 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9092 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9101 /* background drawing hook, to be passed the viewport */
9102 static gboolean tool_sync_done = TRUE;
9104 static gboolean tool_sync(gpointer data)
9106 VikViewport *vvp = data;
9107 gdk_threads_enter();
9108 vik_viewport_sync(vvp);
9109 tool_sync_done = TRUE;
9110 gdk_threads_leave();
9114 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9117 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9118 gdk_gc_set_function ( t->gc, GDK_INVERT );
9119 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9120 vik_viewport_sync(t->vvp);
9125 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9127 VikViewport *vvp = t->vvp;
9128 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9129 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9133 if (tool_sync_done) {
9134 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9135 tool_sync_done = FALSE;
9139 static void marker_end_move ( tool_ed_t *t )
9141 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9142 g_object_unref ( t->gc );
9146 /*** Edit waypoint ****/
9148 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9150 tool_ed_t *t = g_new(tool_ed_t, 1);
9156 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9161 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9163 WPSearchParams params;
9164 tool_ed_t *t = data;
9165 VikViewport *vvp = t->vvp;
9167 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9174 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9177 if ( vtl->current_wp && vtl->current_wp->visible )
9179 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9181 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9183 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9184 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9186 if ( event->button == 3 )
9187 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9189 marker_begin_move(t, event->x, event->y);
9196 params.x = event->x;
9197 params.y = event->y;
9198 params.draw_images = vtl->drawimages;
9199 params.closest_wp_id = NULL;
9200 params.closest_wp = NULL;
9201 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9202 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9204 if ( event->button == 3 )
9205 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9207 marker_begin_move(t, event->x, event->y);
9210 else if ( params.closest_wp )
9212 if ( event->button == 3 )
9213 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9215 vtl->waypoint_rightclick = FALSE;
9217 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9219 vtl->current_wp = params.closest_wp;
9220 vtl->current_wp_id = params.closest_wp_id;
9222 /* could make it so don't update if old WP is off screen and new is null but oh well */
9223 vik_layer_emit_update ( VIK_LAYER(vtl) );
9227 vtl->current_wp = NULL;
9228 vtl->current_wp_id = NULL;
9229 vtl->waypoint_rightclick = FALSE;
9230 vik_layer_emit_update ( VIK_LAYER(vtl) );
9234 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9236 tool_ed_t *t = data;
9237 VikViewport *vvp = t->vvp;
9239 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9244 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9247 if ( event->state & GDK_CONTROL_MASK )
9249 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9251 new_coord = tp->coord;
9255 if ( event->state & GDK_SHIFT_MASK )
9257 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9258 if ( wp && wp != vtl->current_wp )
9259 new_coord = wp->coord;
9264 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9266 marker_moveto ( t, x, y );
9273 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9275 tool_ed_t *t = data;
9276 VikViewport *vvp = t->vvp;
9278 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9281 if ( t->holding && event->button == 1 )
9284 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9287 if ( event->state & GDK_CONTROL_MASK )
9289 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9291 new_coord = tp->coord;
9295 if ( event->state & GDK_SHIFT_MASK )
9297 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9298 if ( wp && wp != vtl->current_wp )
9299 new_coord = wp->coord;
9302 marker_end_move ( t );
9304 vtl->current_wp->coord = new_coord;
9306 trw_layer_calculate_bounds_waypoints ( vtl );
9307 vik_layer_emit_update ( VIK_LAYER(vtl) );
9310 /* PUT IN RIGHT PLACE!!! */
9311 if ( event->button == 3 && vtl->waypoint_rightclick )
9313 if ( vtl->wp_right_click_menu )
9314 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9315 if ( vtl->current_wp ) {
9316 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9317 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 );
9318 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9320 vtl->waypoint_rightclick = FALSE;
9325 /*** New track ****/
9327 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9334 GdkDrawable *drawable;
9340 * Draw specified pixmap
9342 static gboolean draw_sync ( gpointer data )
9344 draw_sync_t *ds = (draw_sync_t*) data;
9345 // Sometimes don't want to draw
9346 // normally because another update has taken precedent such as panning the display
9347 // which means this pixmap is no longer valid
9348 if ( ds->vtl->draw_sync_do ) {
9349 gdk_threads_enter();
9350 gdk_draw_drawable (ds->drawable,
9353 0, 0, 0, 0, -1, -1);
9354 ds->vtl->draw_sync_done = TRUE;
9355 gdk_threads_leave();
9361 static gchar* distance_string (gdouble distance)
9365 /* draw label with distance */
9366 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9367 switch (dist_units) {
9368 case VIK_UNITS_DISTANCE_MILES:
9369 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9370 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9371 } else if (distance < 1609.4) {
9372 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9374 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9378 // VIK_UNITS_DISTANCE_KILOMETRES
9379 if (distance >= 1000 && distance < 100000) {
9380 g_sprintf(str, "%3.2f km", distance/1000.0);
9381 } else if (distance < 1000) {
9382 g_sprintf(str, "%d m", (int)distance);
9384 g_sprintf(str, "%d km", (int)distance/1000);
9388 return g_strdup (str);
9392 * Actually set the message in statusbar
9394 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9396 // Only show elevation data when track has some elevation properties
9397 gchar str_gain_loss[64];
9398 str_gain_loss[0] = '\0';
9399 gchar str_last_step[64];
9400 str_last_step[0] = '\0';
9401 gchar *str_total = distance_string (distance);
9403 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9404 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9405 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9407 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9410 if ( last_step > 0 ) {
9411 gchar *tmp = distance_string (last_step);
9412 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9416 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9418 // Write with full gain/loss information
9419 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9420 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9422 g_free ( str_total );
9426 * Figure out what information should be set in the statusbar and then write it
9428 static void update_statusbar ( VikTrwLayer *vtl )
9430 // Get elevation data
9431 gdouble elev_gain, elev_loss;
9432 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9434 /* Find out actual distance of current track */
9435 gdouble distance = vik_track_get_length (vtl->current_track);
9437 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9441 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9443 /* if we haven't sync'ed yet, we don't have time to do more. */
9444 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9445 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9447 static GdkPixmap *pixmap = NULL;
9449 // Need to check in case window has been resized
9450 w1 = vik_viewport_get_width(vvp);
9451 h1 = vik_viewport_get_height(vvp);
9453 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9455 gdk_drawable_get_size (pixmap, &w2, &h2);
9456 if (w1 != w2 || h1 != h2) {
9457 g_object_unref ( G_OBJECT ( pixmap ) );
9458 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9461 // Reset to background
9462 gdk_draw_drawable (pixmap,
9463 vtl->current_track_newpoint_gc,
9464 vik_viewport_get_pixmap(vvp),
9465 0, 0, 0, 0, -1, -1);
9467 draw_sync_t *passalong;
9470 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9472 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9473 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9474 // thus when we come to reset to the background it would include what we have already drawn!!
9475 gdk_draw_line ( pixmap,
9476 vtl->current_track_newpoint_gc,
9477 x1, y1, event->x, event->y );
9478 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9480 /* Find out actual distance of current track */
9481 gdouble distance = vik_track_get_length (vtl->current_track);
9483 // Now add distance to where the pointer is //
9486 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9487 vik_coord_to_latlon ( &coord, &ll );
9488 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9489 distance = distance + last_step;
9491 // Get elevation data
9492 gdouble elev_gain, elev_loss;
9493 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9495 // Adjust elevation data (if available) for the current pointer position
9497 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9498 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9499 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9500 // Adjust elevation of last track point
9501 if ( elev_new > last_tpt->altitude )
9503 elev_gain += elev_new - last_tpt->altitude;
9506 elev_loss += last_tpt->altitude - elev_new;
9511 // Display of the distance 'tooltip' during track creation is controlled by a preference
9513 if ( a_vik_get_create_track_tooltip() ) {
9515 gchar *str = distance_string (distance);
9517 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9518 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9519 pango_layout_set_text (pl, str, -1);
9521 pango_layout_get_pixel_size ( pl, &wd, &hd );
9524 // offset from cursor a bit depending on font size
9528 // Create a background block to make the text easier to read over the background map
9529 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9530 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9531 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9533 g_object_unref ( G_OBJECT ( pl ) );
9534 g_object_unref ( G_OBJECT ( background_block_gc ) );
9538 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9539 passalong->vtl = vtl;
9540 passalong->pixmap = pixmap;
9541 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9542 passalong->gc = vtl->current_track_newpoint_gc;
9546 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9548 // Update statusbar with full gain/loss information
9549 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9551 // draw pixmap when we have time to
9552 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9553 vtl->draw_sync_done = FALSE;
9554 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9556 return VIK_LAYER_TOOL_ACK;
9559 // NB vtl->current_track must be valid
9560 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9563 if ( vtl->current_track->trackpoints ) {
9564 // TODO rework this...
9565 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9566 GList *last = g_list_last(vtl->current_track->trackpoints);
9567 g_free ( last->data );
9568 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9570 vik_track_calculate_bounds ( vtl->current_track );
9574 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9576 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9577 vtl->current_track = NULL;
9578 vik_layer_emit_update ( VIK_LAYER(vtl) );
9580 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9581 undo_trackpoint_add ( vtl );
9582 update_statusbar ( vtl );
9583 vik_layer_emit_update ( VIK_LAYER(vtl) );
9590 * Common function to handle trackpoint button requests on either a route or a track
9591 * . enables adding a point via normal click
9592 * . enables removal of last point via right click
9593 * . finishing of the track or route via double clicking
9595 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9599 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9602 if ( event->button == 2 ) {
9603 // As the display is panning, the new track pixmap is now invalid so don't draw it
9604 // otherwise this drawing done results in flickering back to an old image
9605 vtl->draw_sync_do = FALSE;
9609 if ( event->button == 3 )
9611 if ( !vtl->current_track )
9613 undo_trackpoint_add ( vtl );
9614 update_statusbar ( vtl );
9615 vik_layer_emit_update ( VIK_LAYER(vtl) );
9619 if ( event->type == GDK_2BUTTON_PRESS )
9621 /* subtract last (duplicate from double click) tp then end */
9622 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9624 /* undo last, then end */
9625 undo_trackpoint_add ( vtl );
9626 vtl->current_track = NULL;
9628 vik_layer_emit_update ( VIK_LAYER(vtl) );
9632 tp = vik_trackpoint_new();
9633 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9635 /* snap to other TP */
9636 if ( event->state & GDK_CONTROL_MASK )
9638 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9640 tp->coord = other_tp->coord;
9643 tp->newsegment = FALSE;
9644 tp->has_timestamp = FALSE;
9647 if ( vtl->current_track ) {
9648 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9649 /* Auto attempt to get elevation from DEM data (if it's available) */
9650 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9653 vtl->ct_x1 = vtl->ct_x2;
9654 vtl->ct_y1 = vtl->ct_y2;
9655 vtl->ct_x2 = event->x;
9656 vtl->ct_y2 = event->y;
9658 vik_layer_emit_update ( VIK_LAYER(vtl) );
9662 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9664 // ----------------------------------------------------- if current is a route - switch to new track
9665 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9667 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9668 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9670 new_track_create_common ( vtl, name );
9676 return tool_new_track_or_route_click ( vtl, event, vvp );
9679 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9681 if ( event->button == 2 ) {
9682 // Pan moving ended - enable potential point drawing again
9683 vtl->draw_sync_do = TRUE;
9684 vtl->draw_sync_done = TRUE;
9688 /*** New route ****/
9690 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9695 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9697 // -------------------------- if current is a track - switch to new route
9698 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9700 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9701 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9702 new_route_create_common ( vtl, name );
9708 return tool_new_track_or_route_click ( vtl, event, vvp );
9711 /*** New waypoint ****/
9713 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9718 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9721 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9723 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9724 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9725 trw_layer_calculate_bounds_waypoints ( vtl );
9726 vik_layer_emit_update ( VIK_LAYER(vtl) );
9732 /*** Edit trackpoint ****/
9734 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9736 tool_ed_t *t = g_new(tool_ed_t, 1);
9742 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9747 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9749 tool_ed_t *t = data;
9750 VikViewport *vvp = t->vvp;
9751 TPSearchParams params;
9752 /* OUTDATED DOCUMENTATION:
9753 find 5 pixel range on each side. then put these UTM, and a pointer
9754 to the winning track name (and maybe the winning track itself), and a
9755 pointer to the winning trackpoint, inside an array or struct. pass
9756 this along, do a foreach on the tracks which will do a foreach on the
9759 params.x = event->x;
9760 params.y = event->y;
9761 params.closest_track_id = NULL;
9762 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9763 params.closest_tp = NULL;
9764 params.closest_tpl = NULL;
9765 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9767 if ( event->button != 1 )
9770 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9773 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9776 if ( vtl->current_tpl )
9778 /* 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.) */
9779 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9780 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9785 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9787 if ( current_tr->visible &&
9788 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9789 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9790 marker_begin_move ( t, event->x, event->y );
9796 if ( vtl->tracks_visible )
9797 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9799 if ( params.closest_tp )
9801 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9802 vtl->current_tpl = params.closest_tpl;
9803 vtl->current_tp_id = params.closest_track_id;
9804 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9805 trw_layer_tpwin_init ( vtl );
9806 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9807 vik_layer_emit_update ( VIK_LAYER(vtl) );
9811 if ( vtl->routes_visible )
9812 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9814 if ( params.closest_tp )
9816 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9817 vtl->current_tpl = params.closest_tpl;
9818 vtl->current_tp_id = params.closest_track_id;
9819 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9820 trw_layer_tpwin_init ( vtl );
9821 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9822 vik_layer_emit_update ( VIK_LAYER(vtl) );
9826 /* these aren't the droids you're looking for */
9830 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9832 tool_ed_t *t = data;
9833 VikViewport *vvp = t->vvp;
9835 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9841 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9844 if ( event->state & GDK_CONTROL_MASK )
9846 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9847 if ( tp && tp != vtl->current_tpl->data )
9848 new_coord = tp->coord;
9850 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9853 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9854 marker_moveto ( t, x, y );
9862 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9864 tool_ed_t *t = data;
9865 VikViewport *vvp = t->vvp;
9867 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9869 if ( event->button != 1)
9874 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9877 if ( event->state & GDK_CONTROL_MASK )
9879 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9880 if ( tp && tp != vtl->current_tpl->data )
9881 new_coord = tp->coord;
9884 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9885 if ( vtl->current_tp_track )
9886 vik_track_calculate_bounds ( vtl->current_tp_track );
9888 marker_end_move ( t );
9890 /* diff dist is diff from orig */
9892 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9894 vik_layer_emit_update ( VIK_LAYER(vtl) );
9901 /*** Route Finder ***/
9902 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9907 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9910 if ( !vtl ) return FALSE;
9911 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9912 if ( event->button == 3 && vtl->route_finder_current_track ) {
9914 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9916 vtl->route_finder_coord = *new_end;
9918 vik_layer_emit_update ( VIK_LAYER(vtl) );
9919 /* remove last ' to:...' */
9920 if ( vtl->route_finder_current_track->comment ) {
9921 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9922 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9923 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9924 last_to - vtl->route_finder_current_track->comment - 1);
9925 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9930 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9931 struct LatLon start, end;
9933 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9934 vik_coord_to_latlon ( &(tmp), &end );
9935 vtl->route_finder_coord = tmp; /* for continuations */
9937 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9938 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9939 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9941 vtl->route_finder_check_added_track = TRUE;
9942 vtl->route_finder_started = FALSE;
9945 vik_routing_default_find ( vtl, start, end);
9947 /* see if anything was done -- a track was added or appended to */
9948 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9949 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 ) );
9950 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9951 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9952 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9953 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9956 if ( vtl->route_finder_added_track )
9957 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9959 vtl->route_finder_added_track = NULL;
9960 vtl->route_finder_check_added_track = FALSE;
9961 vtl->route_finder_append = FALSE;
9963 vik_layer_emit_update ( VIK_LAYER(vtl) );
9965 vtl->route_finder_started = TRUE;
9966 vtl->route_finder_coord = tmp;
9967 vtl->route_finder_current_track = NULL;
9972 /*** Show picture ****/
9974 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9979 /* Params are: vvp, event, last match found or NULL */
9980 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9982 if ( wp->image && wp->visible )
9984 gint x, y, slackx, slacky;
9985 GdkEventButton *event = (GdkEventButton *) params[1];
9987 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9988 slackx = wp->image_width / 2;
9989 slacky = wp->image_height / 2;
9990 if ( x <= event->x + slackx && x >= event->x - slackx
9991 && y <= event->y + slacky && y >= event->y - slacky )
9993 params[2] = wp->image; /* we've found a match. however continue searching
9994 * since we want to find the last match -- that
9995 * is, the match that was drawn last. */
10000 static void trw_layer_show_picture ( menu_array_sublayer values )
10002 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10004 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10005 #else /* WINDOWS */
10006 GError *err = NULL;
10007 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10008 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10009 g_free ( quoted_file );
10010 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10012 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() );
10013 g_error_free ( err );
10016 #endif /* WINDOWS */
10019 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10021 gpointer params[3] = { vvp, event, NULL };
10022 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10024 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10027 static menu_array_sublayer values;
10028 values[MA_VTL] = vtl;
10029 values[MA_MISC] = params[2];
10030 trw_layer_show_picture ( values );
10031 return TRUE; /* found a match */
10034 return FALSE; /* go through other layers, searching for a match */
10037 /***************************************************************************
10039 ***************************************************************************/
10042 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10044 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10045 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10048 /* Structure for thumbnail creating data used in the background thread */
10050 VikTrwLayer *vtl; // Layer needed for redrawing
10051 GSList *pics; // Image list
10052 } thumbnail_create_thread_data;
10054 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10056 guint total = g_slist_length(tctd->pics), done = 0;
10057 while ( tctd->pics )
10059 a_thumbnails_create ( (gchar *) tctd->pics->data );
10060 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10062 return -1; /* Abort thread */
10064 tctd->pics = tctd->pics->next;
10067 // Redraw to show the thumbnails as they are now created
10068 if ( IS_VIK_LAYER(tctd->vtl) )
10069 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10074 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10076 while ( tctd->pics )
10078 g_free ( tctd->pics->data );
10079 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10084 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10086 if ( ! vtl->has_verified_thumbnails )
10088 GSList *pics = NULL;
10089 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10092 gint len = g_slist_length ( pics );
10093 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10094 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10097 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10099 (vik_thr_func) create_thumbnails_thread,
10101 (vik_thr_free_func) thumbnail_create_thread_free,
10109 static const gchar* my_track_colors ( gint ii )
10111 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10123 // Fast and reliable way of returning a colour
10124 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10127 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10129 GHashTableIter iter;
10130 gpointer key, value;
10134 g_hash_table_iter_init ( &iter, vtl->tracks );
10136 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10138 // Tracks get a random spread of colours if not already assigned
10139 if ( ! VIK_TRACK(value)->has_color ) {
10140 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10141 VIK_TRACK(value)->color = vtl->track_color;
10143 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10145 VIK_TRACK(value)->has_color = TRUE;
10148 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10151 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10157 g_hash_table_iter_init ( &iter, vtl->routes );
10159 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10161 // Routes get an intermix of reds
10162 if ( ! VIK_TRACK(value)->has_color ) {
10164 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10166 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10167 VIK_TRACK(value)->has_color = TRUE;
10170 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10177 * (Re)Calculate the bounds of the waypoints in this layer,
10178 * This should be called whenever waypoints are changed
10180 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10182 struct LatLon topleft = { 0.0, 0.0 };
10183 struct LatLon bottomright = { 0.0, 0.0 };
10186 GHashTableIter iter;
10187 gpointer key, value;
10189 g_hash_table_iter_init ( &iter, vtl->waypoints );
10191 // Set bounds to first point
10192 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10193 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10194 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10197 // Ensure there is another point...
10198 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10200 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10202 // See if this point increases the bounds.
10203 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10205 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10206 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10207 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10208 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10212 vtl->waypoints_bbox.north = topleft.lat;
10213 vtl->waypoints_bbox.east = bottomright.lon;
10214 vtl->waypoints_bbox.south = bottomright.lat;
10215 vtl->waypoints_bbox.west = topleft.lon;
10218 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10220 vik_track_calculate_bounds ( trk );
10223 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10225 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10226 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10229 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10231 if ( ! VIK_LAYER(vtl)->vt )
10234 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10235 if ( g_hash_table_size (vtl->tracks) > 1 )
10236 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10238 if ( g_hash_table_size (vtl->routes) > 1 )
10239 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10241 if ( g_hash_table_size (vtl->waypoints) > 1 )
10242 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10245 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10247 if ( VIK_LAYER(vtl)->realized )
10248 trw_layer_verify_thumbnails ( vtl, vvp );
10249 trw_layer_track_alloc_colors ( vtl );
10251 trw_layer_calculate_bounds_waypoints ( vtl );
10252 trw_layer_calculate_bounds_tracks ( vtl );
10254 // Apply treeview sort after loading all the tracks for this layer
10255 // (rather than sorted insert on each individual track additional)
10256 // and after subsequent changes to the properties as the specified order may have changed.
10257 // since the sorting of a treeview section is now very quick
10258 // NB sorting is also performed after every name change as well to maintain the list order
10259 trw_layer_sort_all ( vtl );
10261 // Setting metadata time if not otherwise set
10262 if ( vtl->metadata ) {
10264 gboolean need_to_set_time = TRUE;
10265 if ( vtl->metadata->timestamp ) {
10266 need_to_set_time = FALSE;
10267 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10268 need_to_set_time = TRUE;
10271 if ( need_to_set_time ) {
10272 // Could rewrite this as a general get first time of a TRW Layer function
10273 GTimeVal timestamp;
10274 timestamp.tv_usec = 0;
10275 gboolean has_timestamp = FALSE;
10278 gl = g_hash_table_get_values ( vtl->tracks );
10279 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10280 gl = g_list_first ( gl );
10282 // Check times of tracks
10284 // Only need to check the first track as they have been sorted by time
10285 VikTrack *trk = (VikTrack*)gl->data;
10286 // Assume trackpoints already sorted by time
10287 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10288 if ( tpt && tpt->has_timestamp ) {
10289 timestamp.tv_sec = tpt->timestamp;
10290 has_timestamp = TRUE;
10292 g_list_free ( gl );
10295 if ( !has_timestamp ) {
10296 // 'Last' resort - current time
10297 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10298 g_get_current_time ( ×tamp );
10300 // Check times of waypoints
10301 gl = g_hash_table_get_values ( vtl->waypoints );
10303 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10304 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10305 if ( wpt->has_timestamp ) {
10306 if ( timestamp.tv_sec > wpt->timestamp ) {
10307 timestamp.tv_sec = wpt->timestamp;
10308 has_timestamp = TRUE;
10312 g_list_free ( gl );
10315 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10320 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10322 return vtl->coord_mode;
10326 * Uniquify the whole layer
10327 * Also requires the layers panel as the names shown there need updating too
10328 * Returns whether the operation was successful or not
10330 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10332 if ( vtl && vlp ) {
10333 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10334 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10335 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10341 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10343 vik_coord_convert ( &(wp->coord), *dest_mode );
10346 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10348 vik_track_convert ( tr, *dest_mode );
10351 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10353 if ( vtl->coord_mode != dest_mode )
10355 vtl->coord_mode = dest_mode;
10356 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10357 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10358 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10362 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10364 vtl->menu_selection = selection;
10367 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10369 return (vtl->menu_selection);
10372 /* ----------- Downloading maps along tracks --------------- */
10374 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10376 /* TODO: calculating based on current size of viewport */
10377 const gdouble w_at_zoom_0_125 = 0.0013;
10378 const gdouble h_at_zoom_0_125 = 0.0011;
10379 gdouble zoom_factor = zoom_level/0.125;
10381 wh->lat = h_at_zoom_0_125 * zoom_factor;
10382 wh->lon = w_at_zoom_0_125 * zoom_factor;
10384 return 0; /* all OK */
10387 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10389 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10390 (dist->lat >= ABS(to->north_south - from->north_south)))
10393 VikCoord *coord = g_malloc(sizeof(VikCoord));
10394 coord->mode = VIK_COORD_LATLON;
10396 if (ABS(gradient) < 1) {
10397 if (from->east_west > to->east_west)
10398 coord->east_west = from->east_west - dist->lon;
10400 coord->east_west = from->east_west + dist->lon;
10401 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10403 if (from->north_south > to->north_south)
10404 coord->north_south = from->north_south - dist->lat;
10406 coord->north_south = from->north_south + dist->lat;
10407 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10413 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10415 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10416 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10418 VikCoord *next = from;
10420 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10422 list = g_list_prepend(list, next);
10428 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10430 typedef struct _Rect {
10435 #define GLRECT(iter) ((Rect *)((iter)->data))
10438 GList *rects_to_download = NULL;
10441 if (get_download_area_width(vvp, zoom_level, &wh))
10444 GList *iter = tr->trackpoints;
10448 gboolean new_map = TRUE;
10449 VikCoord *cur_coord, tl, br;
10452 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10454 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10455 rect = g_malloc(sizeof(Rect));
10458 rect->center = *cur_coord;
10459 rects_to_download = g_list_prepend(rects_to_download, rect);
10464 gboolean found = FALSE;
10465 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10466 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10477 GList *fillins = NULL;
10478 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10479 /* seems that ATM the function get_next_coord works only for LATLON */
10480 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10481 /* fill-ins for far apart points */
10482 GList *cur_rect, *next_rect;
10483 for (cur_rect = rects_to_download;
10484 (next_rect = cur_rect->next) != NULL;
10485 cur_rect = cur_rect->next) {
10486 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10487 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10488 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10492 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10495 GList *fiter = fillins;
10497 cur_coord = (VikCoord *)(fiter->data);
10498 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10499 rect = g_malloc(sizeof(Rect));
10502 rect->center = *cur_coord;
10503 rects_to_download = g_list_prepend(rects_to_download, rect);
10504 fiter = fiter->next;
10508 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10509 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10513 for (iter = fillins; iter; iter = iter->next)
10514 g_free(iter->data);
10515 g_list_free(fillins);
10517 if (rects_to_download) {
10518 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10519 g_free(rect_iter->data);
10520 g_list_free(rects_to_download);
10524 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10528 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10529 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10530 gint selected_zoom, default_zoom;
10532 VikTrwLayer *vtl = values[MA_VTL];
10533 VikLayersPanel *vlp = values[MA_VLP];
10535 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10536 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10538 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10542 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10544 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10545 int num_maps = g_list_length(vmls);
10548 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10552 // Convert from list of vmls to list of names. Allowing the user to select one of them
10553 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10554 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10556 gchar **np = map_names;
10557 VikMapsLayer **lp = map_layers;
10559 for (i = 0; i < num_maps; i++) {
10560 vml = (VikMapsLayer *)(vmls->data);
10562 *np++ = vik_maps_layer_get_map_label(vml);
10565 // Mark end of the array lists
10569 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10570 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10571 if (cur_zoom == zoom_vals[default_zoom])
10574 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10576 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10579 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10582 for (i = 0; i < num_maps; i++)
10583 g_free(map_names[i]);
10585 g_free(map_layers);
10591 /**** lowest waypoint number calculation ***/
10592 static gint highest_wp_number_name_to_number(const gchar *name) {
10593 if ( strlen(name) == 3 ) {
10594 int n = atoi(name);
10595 if ( n < 100 && name[0] != '0' )
10597 if ( n < 10 && name[0] != '0' )
10605 static void highest_wp_number_reset(VikTrwLayer *vtl)
10607 vtl->highest_wp_number = -1;
10610 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10612 /* if is bigger that top, add it */
10613 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10614 if ( new_wp_num > vtl->highest_wp_number )
10615 vtl->highest_wp_number = new_wp_num;
10618 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10620 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10621 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10622 if ( vtl->highest_wp_number == old_wp_num ) {
10624 vtl->highest_wp_number--;
10626 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10627 /* search down until we find something that *does* exist */
10629 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10630 vtl->highest_wp_number--;
10631 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10636 /* get lowest unused number */
10637 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10640 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10642 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10643 return g_strdup(buf);
10647 * trw_layer_create_track_list_both:
10649 * Create the latest list of tracks and routes
10651 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10653 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10654 GList *tracks = NULL;
10655 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10656 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10658 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10661 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10663 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10665 gchar *title = NULL;
10666 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10667 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10669 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10671 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10675 static void trw_layer_track_list_dialog ( menu_array_layer values )
10677 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10679 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10680 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10684 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10686 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10688 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10689 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );