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 gboolean route_finder_check_added_track;
206 VikTrack *route_finder_added_track;
207 gboolean route_finder_append;
214 guint16 image_cache_size;
216 /* for waypoint text */
217 PangoLayout *wplabellayout;
219 gboolean has_verified_thumbnails;
221 GtkMenu *wp_right_click_menu;
222 GtkMenu *track_right_click_menu;
225 VikStdLayerMenuItem menu_selection;
227 gint highest_wp_number;
230 GtkWidget *tracks_analysis_dialog;
233 /* A caached waypoint image. */
236 gchar *image; /* filename */
239 struct DrawingParams {
244 guint16 width, height;
245 gdouble cc; // Cosine factor in track directions
246 gdouble ss; // Sine factor in track directions
247 const VikCoord *center;
248 gboolean one_zone, lat_lon;
249 gdouble ce1, ce2, cn1, cn2;
254 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
259 MA_SUBTYPE, // OR END for Layer only
268 typedef gpointer menu_array_layer[2];
269 typedef gpointer menu_array_sublayer[MA_LAST];
271 static void trw_layer_delete_item ( menu_array_sublayer values );
272 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
273 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
275 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
276 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
278 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
279 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
281 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
282 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
284 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
285 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
286 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
287 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
288 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
289 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
290 static void trw_layer_goto_track_center ( menu_array_sublayer values );
291 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
292 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
293 static void trw_layer_merge_with_other ( menu_array_sublayer values );
294 static void trw_layer_append_track ( menu_array_sublayer values );
295 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
296 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
297 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
298 static void trw_layer_split_segments ( menu_array_sublayer values );
299 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
300 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
301 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
302 static void trw_layer_reverse ( menu_array_sublayer values );
303 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
304 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
305 static void trw_layer_show_picture ( menu_array_sublayer values );
306 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
308 static void trw_layer_centerize ( menu_array_layer values );
309 static void trw_layer_auto_view ( menu_array_layer values );
310 static void trw_layer_goto_wp ( menu_array_layer values );
311 static void trw_layer_new_wp ( menu_array_layer values );
312 static void trw_layer_new_track ( menu_array_layer values );
313 static void trw_layer_new_route ( menu_array_layer values );
314 static void trw_layer_finish_track ( menu_array_layer values );
315 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
316 static void trw_layer_auto_tracks_view ( menu_array_layer values );
317 static void trw_layer_delete_all_tracks ( menu_array_layer values );
318 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
319 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
320 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
321 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
322 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
323 #ifdef VIK_CONFIG_GEOTAG
324 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
325 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
326 static void trw_layer_geotagging_track ( menu_array_sublayer values );
327 static void trw_layer_geotagging ( menu_array_layer values );
329 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
330 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
331 static void trw_layer_acquire_url_cb ( menu_array_layer values );
332 #ifdef VIK_CONFIG_OPENSTREETMAP
333 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
334 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
336 #ifdef VIK_CONFIG_GEOCACHES
337 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
339 #ifdef VIK_CONFIG_GEOTAG
340 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
342 static void trw_layer_acquire_file_cb ( menu_array_layer values );
343 static void trw_layer_gps_upload ( menu_array_layer values );
345 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
346 static void trw_layer_track_list_dialog ( menu_array_layer values );
347 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
349 // Specific route versions:
350 // Most track handling functions can handle operating on the route list
351 // However these ones are easier in separate functions
352 static void trw_layer_auto_routes_view ( menu_array_layer values );
353 static void trw_layer_delete_all_routes ( menu_array_layer values );
354 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
357 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
358 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
359 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
360 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
362 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
363 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
365 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
366 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
367 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
368 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
370 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
371 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
372 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
373 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
374 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
376 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
377 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
378 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
379 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
380 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
381 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
383 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
384 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
387 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
389 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
390 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
391 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
395 static void cached_pixbuf_free ( CachedPixbuf *cp );
396 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
398 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
399 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
402 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
404 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
405 static void highest_wp_number_reset(VikTrwLayer *vtl);
406 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
407 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
409 // Note for the following tool GtkRadioActionEntry texts:
410 // the very first text value is an internal name not displayed anywhere
411 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
412 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
413 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
414 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
415 static VikToolInterface trw_layer_tools[] = {
416 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
417 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
418 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
420 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
422 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
423 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
424 (VikToolMouseFunc) tool_new_track_click,
425 (VikToolMouseMoveFunc) tool_new_track_move,
426 (VikToolMouseFunc) tool_new_track_release,
427 (VikToolKeyFunc) tool_new_track_key_press,
428 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
429 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
431 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
432 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
433 (VikToolMouseFunc) tool_new_route_click,
434 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
435 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
436 (VikToolKeyFunc) tool_new_track_key_press, // -/#
437 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
438 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
440 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
441 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
442 (VikToolMouseFunc) tool_extended_route_finder_click,
443 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
444 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
445 (VikToolKeyFunc) tool_extended_route_finder_key_press,
446 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
447 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
449 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
450 (VikToolConstructorFunc) tool_edit_waypoint_create,
451 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
453 (VikToolMouseFunc) tool_edit_waypoint_click,
454 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
455 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
457 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
459 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
460 (VikToolConstructorFunc) tool_edit_trackpoint_create,
461 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
463 (VikToolMouseFunc) tool_edit_trackpoint_click,
464 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
465 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
467 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
469 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
470 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
471 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
473 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
478 TOOL_CREATE_WAYPOINT=0,
483 TOOL_EDIT_TRACKPOINT,
488 /****** PARAMETERS ******/
490 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
491 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
493 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
494 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
496 #define MIN_POINT_SIZE 2
497 #define MAX_POINT_SIZE 10
499 #define MIN_ARROW_SIZE 3
500 #define MAX_ARROW_SIZE 20
502 static VikLayerParamScale params_scales[] = {
503 /* min max step digits */
504 { 1, 10, 1, 0 }, /* line_thickness */
505 { 0, 100, 1, 0 }, /* track draw speed factor */
506 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
507 /* 5 * step == how much to turn */
508 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
509 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
510 { 5, 500, 5, 0 }, // 5: image cache_size - " "
511 { 0, 8, 1, 0 }, // 6: Background line thickness
512 { 1, 64, 1, 0 }, /* wpsize */
513 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
514 { 1, 100, 1, 0 }, // 9: elevation factor
515 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
516 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
519 static gchar* params_font_sizes[] = {
520 N_("Extra Extra Small"),
526 N_("Extra Extra Large"),
529 // Needs to align with vik_layer_sort_order_t
530 static gchar* params_sort_order[] = {
532 N_("Name Ascending"),
533 N_("Name Descending"),
537 static VikLayerParamData black_color_default ( void ) {
538 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
540 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
541 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
542 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
543 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
544 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
545 static VikLayerParamData trackbgcolor_default ( void ) {
546 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
548 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
549 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
550 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
552 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
553 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
554 static VikLayerParamData wptextcolor_default ( void ) {
555 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
557 static VikLayerParamData wpbgcolor_default ( void ) {
558 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
560 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
561 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
563 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
564 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
565 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
567 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
569 static VikLayerParamData string_default ( void )
571 VikLayerParamData data;
576 VikLayerParam trw_layer_params[] = {
577 { 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 },
578 { 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 },
579 { 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 },
581 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
582 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
583 { 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 },
584 { 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 },
585 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
586 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
587 { 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 },
588 { 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 },
589 { 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 },
590 { 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 },
591 { 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 },
592 { 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 },
593 { 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 },
594 { 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 },
595 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
596 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 },
597 { 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 },
599 { 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 },
600 { 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 },
601 { 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,
602 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
603 { 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 },
605 { 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 },
606 { 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 },
607 { 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 },
608 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
609 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
610 { 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 },
611 { 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 },
612 { 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 },
613 { 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 },
614 { 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 },
616 { 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 },
617 { 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 },
618 { 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 },
619 { 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 },
621 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
622 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
623 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
627 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
629 // Sublayer visibilities
677 *** 1) Add to trw_layer_params and enumeration
678 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
681 /****** END PARAMETERS ******/
683 /* Layer Interface function definitions */
684 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
685 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
686 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
687 static void trw_layer_free ( VikTrwLayer *trwlayer );
688 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
689 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
690 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
691 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
692 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
693 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
694 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
695 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
696 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
697 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
698 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
699 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
700 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
701 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
702 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
703 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
704 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
705 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
706 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
707 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
708 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
709 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
710 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
711 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
712 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
713 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
714 /* End Layer Interface function definitions */
716 VikLayerInterface vik_trw_layer_interface = {
723 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
727 params_groups, /* params_groups */
728 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
732 (VikLayerFuncCreate) trw_layer_create,
733 (VikLayerFuncRealize) trw_layer_realize,
734 (VikLayerFuncPostRead) trw_layer_post_read,
735 (VikLayerFuncFree) trw_layer_free,
737 (VikLayerFuncProperties) NULL,
738 (VikLayerFuncDraw) trw_layer_draw,
739 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
741 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
742 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
744 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
745 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
747 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
748 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
749 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
750 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
751 (VikLayerFuncLayerSelected) trw_layer_selected,
753 (VikLayerFuncMarshall) trw_layer_marshall,
754 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
756 (VikLayerFuncSetParam) trw_layer_set_param,
757 (VikLayerFuncGetParam) trw_layer_get_param,
758 (VikLayerFuncChangeParam) trw_layer_change_param,
760 (VikLayerFuncReadFileData) a_gpspoint_read_file,
761 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
763 (VikLayerFuncDeleteItem) trw_layer_del_item,
764 (VikLayerFuncCutItem) trw_layer_cut_item,
765 (VikLayerFuncCopyItem) trw_layer_copy_item,
766 (VikLayerFuncPasteItem) trw_layer_paste_item,
767 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
769 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
771 (VikLayerFuncSelectClick) trw_layer_select_click,
772 (VikLayerFuncSelectMove) trw_layer_select_move,
773 (VikLayerFuncSelectRelease) trw_layer_select_release,
774 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
777 static gboolean have_diary_program = FALSE;
779 // NB Only performed once per program run
780 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
782 if ( g_find_program_in_path( "rednotebook" ) ) {
783 gchar *stdout = NULL;
784 gchar *stderr = NULL;
785 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
786 if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
787 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
789 g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
791 g_warning ("Diary: stderr: %s", stderr );
793 gchar **tokens = NULL;
794 if ( stdout && g_strcmp0(stdout, "") )
795 tokens = g_strsplit(stdout, " ", 0);
797 tokens = g_strsplit(stderr, " ", 0);
800 gchar *token = tokens[num];
801 while ( token && num < 2 ) {
803 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
804 have_diary_program = TRUE;
809 g_strfreev ( tokens );
816 GType vik_trw_layer_get_type ()
818 static GType vtl_type = 0;
822 static const GTypeInfo vtl_info =
824 sizeof (VikTrwLayerClass),
825 NULL, /* base_init */
826 NULL, /* base_finalize */
827 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
828 NULL, /* class_finalize */
829 NULL, /* class_data */
830 sizeof (VikTrwLayer),
832 NULL /* instance init */
834 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
839 VikTRWMetadata *vik_trw_metadata_new()
841 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
844 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
849 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
851 return vtl->metadata;
854 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
857 vik_trw_metadata_free ( vtl->metadata );
858 vtl->metadata = metadata;
863 const gchar *date_str;
865 const VikWaypoint *wpt;
870 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
874 // Might be an easier way to compare dates rather than converting the strings all the time...
875 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
876 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
878 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
887 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
891 // Might be an easier way to compare dates rather than converting the strings all the time...
892 if ( wpt->has_timestamp ) {
893 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
895 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
905 * Find an item by date
907 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
911 df.date_str = date_str;
916 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
918 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
920 if ( select && df.found ) {
921 if ( do_tracks && df.trk ) {
922 struct LatLon maxmin[2] = { {0,0}, {0,0} };
923 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
924 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
925 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
928 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
929 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
931 vik_layer_emit_update ( VIK_LAYER(vtl) );
936 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
938 static menu_array_sublayer values;
944 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
947 values[MA_VTL] = vtl;
948 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
949 values[MA_SUBLAYER_ID] = sublayer;
950 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
952 trw_layer_delete_item ( values );
955 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
957 static menu_array_sublayer values;
963 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
966 values[MA_VTL] = vtl;
967 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
968 values[MA_SUBLAYER_ID] = sublayer;
969 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
971 trw_layer_copy_item_cb(values);
972 trw_layer_cut_item_cb(values);
975 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
977 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
978 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
979 gpointer * sublayer = values[MA_SUBLAYER_ID];
983 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
987 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
988 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
989 if ( wp && wp->name )
992 name = NULL; // Broken :(
994 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
995 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
996 if ( trk && trk->name )
999 name = NULL; // Broken :(
1002 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1003 if ( trk && trk->name )
1006 name = NULL; // Broken :(
1009 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1010 subtype, len, name, data);
1014 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1016 trw_layer_copy_item_cb(values);
1017 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1018 trw_layer_delete_item(values);
1021 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1023 // Slightly cheating method, routing via the panels capability
1024 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1027 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1037 GByteArray *ba = g_byte_array_new ();
1039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1040 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1041 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1042 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1044 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1047 g_byte_array_append ( ba, id, il );
1055 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1062 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1066 w = vik_waypoint_unmarshall ( item, len );
1067 // When copying - we'll create a new name based on the original
1068 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1069 vik_trw_layer_add_waypoint ( vtl, name, w );
1070 waypoint_convert (NULL, w, &vtl->coord_mode);
1073 trw_layer_calculate_bounds_waypoints ( vtl );
1075 // Consider if redraw necessary for the new item
1076 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1077 vik_layer_emit_update ( VIK_LAYER(vtl) );
1080 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1084 t = vik_track_unmarshall ( item, len );
1085 // When copying - we'll create a new name based on the original
1086 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1087 vik_trw_layer_add_track ( vtl, name, t );
1088 vik_track_convert (t, vtl->coord_mode);
1091 // Consider if redraw necessary for the new item
1092 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1093 vik_layer_emit_update ( VIK_LAYER(vtl) );
1096 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1100 t = vik_track_unmarshall ( item, len );
1101 // When copying - we'll create a new name based on the original
1102 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1103 vik_trw_layer_add_route ( vtl, name, t );
1104 vik_track_convert (t, vtl->coord_mode);
1107 // Consider if redraw necessary for the new item
1108 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1109 vik_layer_emit_update ( VIK_LAYER(vtl) );
1115 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1122 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1126 case PARAM_TV: vtl->tracks_visible = data.b; break;
1127 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1128 case PARAM_RV: vtl->routes_visible = data.b; break;
1129 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1130 case PARAM_TLFONTSIZE:
1131 if ( data.u < FS_NUM_SIZES ) {
1132 vtl->track_font_size = data.u;
1133 g_free ( vtl->track_fsize_str );
1134 switch ( vtl->track_font_size ) {
1135 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1136 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1137 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1138 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1139 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1140 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1141 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1145 case PARAM_DM: vtl->drawmode = data.u; break;
1147 vtl->track_color = data.c;
1148 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1150 case PARAM_DP: vtl->drawpoints = data.b; break;
1152 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1153 vtl->drawpoints_size = data.u;
1155 case PARAM_DE: vtl->drawelevation = data.b; break;
1156 case PARAM_DS: vtl->drawstops = data.b; break;
1157 case PARAM_DL: vtl->drawlines = data.b; break;
1158 case PARAM_DD: vtl->drawdirections = data.b; break;
1160 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1161 vtl->drawdirections_size = data.u;
1163 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1164 vtl->stop_length = data.u;
1166 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1167 vtl->elevation_factor = data.u;
1169 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1171 vtl->line_thickness = data.u;
1172 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1175 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1177 vtl->bg_line_thickness = data.u;
1178 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1182 vtl->track_bg_color = data.c;
1183 if ( vtl->track_bg_gc )
1184 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1186 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1187 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1188 case PARAM_DLA: vtl->drawlabels = data.b; break;
1189 case PARAM_DI: vtl->drawimages = data.b; break;
1190 case PARAM_IS: if ( data.u != vtl->image_size )
1192 vtl->image_size = data.u;
1193 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1194 g_queue_free ( vtl->image_cache );
1195 vtl->image_cache = g_queue_new ();
1198 case PARAM_IA: vtl->image_alpha = data.u; break;
1199 case PARAM_ICS: vtl->image_cache_size = data.u;
1200 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1201 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1204 vtl->waypoint_color = data.c;
1205 if ( vtl->waypoint_gc )
1206 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1209 vtl->waypoint_text_color = data.c;
1210 if ( vtl->waypoint_text_gc )
1211 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1214 vtl->waypoint_bg_color = data.c;
1215 if ( vtl->waypoint_bg_gc )
1216 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1219 vtl->wpbgand = data.b;
1220 if ( vtl->waypoint_bg_gc )
1221 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1223 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1224 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1225 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1226 case PARAM_WPFONTSIZE:
1227 if ( data.u < FS_NUM_SIZES ) {
1228 vtl->wp_font_size = data.u;
1229 g_free ( vtl->wp_fsize_str );
1230 switch ( vtl->wp_font_size ) {
1231 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1232 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1233 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1234 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1235 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1236 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1237 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1241 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1243 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1244 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1245 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1246 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1252 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1254 VikLayerParamData rv;
1257 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1258 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1259 case PARAM_RV: rv.b = vtl->routes_visible; break;
1260 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1261 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1262 case PARAM_DM: rv.u = vtl->drawmode; break;
1263 case PARAM_TC: rv.c = vtl->track_color; break;
1264 case PARAM_DP: rv.b = vtl->drawpoints; break;
1265 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1266 case PARAM_DE: rv.b = vtl->drawelevation; break;
1267 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1268 case PARAM_DS: rv.b = vtl->drawstops; break;
1269 case PARAM_SL: rv.u = vtl->stop_length; break;
1270 case PARAM_DL: rv.b = vtl->drawlines; break;
1271 case PARAM_DD: rv.b = vtl->drawdirections; break;
1272 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1273 case PARAM_LT: rv.u = vtl->line_thickness; break;
1274 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1275 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1276 case PARAM_DI: rv.b = vtl->drawimages; break;
1277 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1278 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1279 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1280 case PARAM_IS: rv.u = vtl->image_size; break;
1281 case PARAM_IA: rv.u = vtl->image_alpha; break;
1282 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1283 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1284 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1285 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1286 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1287 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1288 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1289 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1290 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1291 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1293 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1294 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1295 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1296 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1302 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1304 // This '-3' is to account for the first few parameters not in the properties
1305 const gint OFFSET = -3;
1307 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1308 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1311 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1312 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1313 GtkWidget **ww2 = values[UI_CHG_LABELS];
1314 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1315 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1316 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1317 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1318 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1319 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1320 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1321 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1322 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1323 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1324 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1325 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1328 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1331 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1332 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1333 GtkWidget **ww2 = values[UI_CHG_LABELS];
1334 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1335 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1336 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1337 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1338 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1339 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1340 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1341 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1342 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1343 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1344 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1345 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1346 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1347 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1348 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1349 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1352 // Alter sensitivity of all track colours according to the draw track mode.
1355 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1356 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1357 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1358 GtkWidget **ww2 = values[UI_CHG_LABELS];
1359 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1360 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1361 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1362 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1365 case PARAM_MDTIME: {
1366 // Force metadata->timestamp to be always read-only for now.
1367 GtkWidget **ww = values[UI_CHG_WIDGETS];
1368 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1369 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1371 // NB Since other track settings have been split across tabs,
1372 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1377 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1384 // Use byte arrays to store sublayer data
1385 // much like done elsewhere e.g. vik_layer_marshall_params()
1386 GByteArray *ba = g_byte_array_new ( );
1391 guint object_length;
1394 // the length of the item
1395 // the sublayer type of item
1396 // the the actual item
1397 #define tlm_append(object_pointer, size, type) \
1399 object_length = (size); \
1400 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1401 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1402 g_byte_array_append ( ba, (object_pointer), object_length );
1404 // Layer parameters first
1405 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1406 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1407 g_byte_array_append ( ba, pd, pl );
1410 // Now sublayer data
1411 GHashTableIter iter;
1412 gpointer key, value;
1415 g_hash_table_iter_init ( &iter, vtl->waypoints );
1416 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1417 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1418 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1423 g_hash_table_iter_init ( &iter, vtl->tracks );
1424 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1425 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1426 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1431 g_hash_table_iter_init ( &iter, vtl->routes );
1432 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1433 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1434 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1444 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1446 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1448 gint consumed_length;
1450 // First the overall layer parameters
1451 memcpy(&pl, data, sizeof(pl));
1453 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1456 consumed_length = pl;
1457 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1459 #define tlm_size (*(gint *)data)
1460 // See marshalling above for order of how this is written
1462 data += sizeof_len_and_subtype + tlm_size;
1464 // Now the individual sublayers:
1466 while ( *data && consumed_length < len ) {
1467 // Normally four extra bytes at the end of the datastream
1468 // (since it's a GByteArray and that's where it's length is stored)
1469 // So only attempt read when there's an actual block of sublayer data
1470 if ( consumed_length + tlm_size < len ) {
1472 // Reuse pl to read the subtype from the data stream
1473 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1475 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1476 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1477 gchar *name = g_strdup ( trk->name );
1478 vik_trw_layer_add_track ( vtl, name, trk );
1481 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1482 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1483 gchar *name = g_strdup ( wp->name );
1484 vik_trw_layer_add_waypoint ( vtl, name, wp );
1487 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1488 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1489 gchar *name = g_strdup ( trk->name );
1490 vik_trw_layer_add_route ( vtl, name, trk );
1494 consumed_length += tlm_size + sizeof_len_and_subtype;
1497 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1499 // Not stored anywhere else so need to regenerate
1500 trw_layer_calculate_bounds_waypoints ( vtl );
1505 // Keep interesting hash function at least visible
1507 static guint strcase_hash(gconstpointer v)
1509 // 31 bit hash function
1512 gchar s[128]; // malloc is too slow for reading big files
1515 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1516 p[i] = toupper(t[i]);
1522 for (p += 1; *p != '\0'; p++)
1523 h = (h << 5) - h + *p;
1530 // Stick a 1 at the end of the function name to make it more unique
1531 // thus more easily searchable in a simple text editor
1532 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1534 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1535 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1537 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1538 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1540 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1541 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1542 // and with normal PC processing capabilities - it has negligibile performance impact
1543 // This also minimized the amount of rework - as the management of the hash tables already exists.
1545 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1546 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1547 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1549 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1550 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1551 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1552 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1553 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1554 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1556 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1558 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1560 // Param settings that are not available via the GUI
1561 // Force to on after processing params (which defaults them to off with a zero value)
1562 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1564 rv->metadata = vik_trw_metadata_new ();
1565 rv->draw_sync_done = TRUE;
1566 rv->draw_sync_do = TRUE;
1567 // Everything else is 0, FALSE or NULL
1573 static void trw_layer_free ( VikTrwLayer *trwlayer )
1575 g_hash_table_destroy(trwlayer->waypoints);
1576 g_hash_table_destroy(trwlayer->waypoints_iters);
1577 g_hash_table_destroy(trwlayer->tracks);
1578 g_hash_table_destroy(trwlayer->tracks_iters);
1579 g_hash_table_destroy(trwlayer->routes);
1580 g_hash_table_destroy(trwlayer->routes_iters);
1582 /* ODC: replace with GArray */
1583 trw_layer_free_track_gcs ( trwlayer );
1585 if ( trwlayer->wp_right_click_menu )
1586 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1588 if ( trwlayer->track_right_click_menu )
1589 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1591 if ( trwlayer->tracklabellayout != NULL)
1592 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1594 if ( trwlayer->wplabellayout != NULL)
1595 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1597 if ( trwlayer->waypoint_gc != NULL )
1598 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1600 if ( trwlayer->waypoint_text_gc != NULL )
1601 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1603 if ( trwlayer->waypoint_bg_gc != NULL )
1604 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1606 g_free ( trwlayer->wp_fsize_str );
1607 g_free ( trwlayer->track_fsize_str );
1609 if ( trwlayer->tpwin != NULL )
1610 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1612 if ( trwlayer->tracks_analysis_dialog != NULL )
1613 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1615 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1616 g_queue_free ( trwlayer->image_cache );
1619 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1623 dp->highlight = highlight;
1624 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1625 dp->xmpp = vik_viewport_get_xmpp ( vp );
1626 dp->ympp = vik_viewport_get_ympp ( vp );
1627 dp->width = vik_viewport_get_width ( vp );
1628 dp->height = vik_viewport_get_height ( vp );
1629 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1630 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1632 dp->center = vik_viewport_get_center ( vp );
1633 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1634 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1639 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1640 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1641 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1643 dp->ce1 = dp->center->east_west-w2;
1644 dp->ce2 = dp->center->east_west+w2;
1645 dp->cn1 = dp->center->north_south-h2;
1646 dp->cn2 = dp->center->north_south+h2;
1647 } else if ( dp->lat_lon ) {
1648 VikCoord upperleft, bottomright;
1649 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1650 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1651 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1652 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1653 dp->ce1 = upperleft.east_west;
1654 dp->ce2 = bottomright.east_west;
1655 dp->cn1 = bottomright.north_south;
1656 dp->cn2 = upperleft.north_south;
1659 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1663 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1664 * Here a simple traffic like light colour system is used:
1665 * . slow points are red
1666 * . average is yellow
1667 * . fast points are green
1669 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1672 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1673 if ( average_speed > 0 ) {
1674 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1675 if ( rv < low_speed )
1676 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1677 else if ( rv > high_speed )
1678 return VIK_TRW_LAYER_TRACK_GC_FAST;
1680 return VIK_TRW_LAYER_TRACK_GC_AVER;
1683 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1686 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1688 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1689 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1690 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1691 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1695 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1697 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1699 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1700 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1702 // Fallback if parse failure
1703 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1705 g_free ( label_markup );
1707 gint label_x, label_y;
1709 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1711 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1712 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1716 * distance_in_preferred_units:
1717 * @dist: The source distance in standard SI Units (i.e. metres)
1719 * TODO: This is a generic function that could be moved into globals.c or utils.c
1721 * Probably best used if you have a only few conversions to perform.
1722 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1723 * since it will be doing the preference check on each call
1725 * Returns: The distance in the units as specified by the preferences
1727 static gdouble distance_in_preferred_units ( gdouble dist )
1730 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1731 switch (dist_units) {
1732 case VIK_UNITS_DISTANCE_MILES:
1733 mydist = VIK_METERS_TO_MILES(dist);
1735 // VIK_UNITS_DISTANCE_KILOMETRES:
1737 mydist = dist/1000.0;
1744 * trw_layer_draw_dist_labels:
1746 * Draw a few labels along a track at nicely seperated distances
1747 * This might slow things down if there's many tracks being displayed with this on.
1749 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1751 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1752 25.0, 40.0, 50.0, 75.0, 100.0,
1753 150.0, 200.0, 250.0, 500.0, 1000.0};
1755 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1757 // Convert to specified unit to find the friendly breakdown value
1758 dist = distance_in_preferred_units ( dist );
1762 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1763 if ( chunksd[i] > dist ) {
1765 dist = chunksd[index];
1770 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1772 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1773 gdouble dist_i = dist * i;
1775 // Convert distance back into metres for use in finding a trackpoint
1776 switch (dist_units) {
1777 case VIK_UNITS_DISTANCE_MILES:
1778 dist_i = VIK_MILES_TO_METERS(dist_i);
1780 // VIK_UNITS_DISTANCE_KILOMETRES:
1782 dist_i = dist_i*1000.0;
1786 gdouble dist_current = 0.0;
1787 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1788 gdouble dist_next = 0.0;
1789 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1791 gdouble dist_between_tps = fabs (dist_next - dist_current);
1792 gdouble ratio = 0.0;
1793 // Prevent division by 0 errors
1794 if ( dist_between_tps > 0.0 )
1795 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1797 if ( tp_current && tp_next ) {
1798 // Construct the name based on the distance value
1801 switch (dist_units) {
1802 case VIK_UNITS_DISTANCE_MILES:
1803 units = g_strdup ( _("miles") );
1805 // VIK_UNITS_DISTANCE_KILOMETRES:
1807 units = g_strdup ( _("km") );
1811 // Convert for display
1812 dist_i = distance_in_preferred_units ( dist_i );
1814 // Make the precision of the output related to the unit size.
1816 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1817 else if ( index == 1 )
1818 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1820 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1823 struct LatLon ll_current, ll_next;
1824 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1825 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1827 // positional interpolation
1828 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1829 // but should be good enough over the small scale that I anticipate usage on
1830 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1831 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1833 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1836 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1837 fgcolour = gdk_color_to_string ( &(trk->color) );
1839 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1841 // if highlight mode on, then colour the background in the highlight colour
1843 if ( drawing_highlight )
1844 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1846 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1848 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1850 g_free ( fgcolour );
1851 g_free ( bgcolour );
1858 * trw_layer_draw_track_name_labels:
1860 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1862 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1865 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1866 fgcolour = gdk_color_to_string ( &(trk->color) );
1868 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1870 // if highlight mode on, then colour the background in the highlight colour
1872 if ( drawing_highlight )
1873 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1875 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1877 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1879 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1880 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1881 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1882 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1883 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1884 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1886 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1888 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1891 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1892 // No other labels to draw
1895 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1898 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1901 VikCoord begin_coord = tp_begin->coord;
1902 VikCoord end_coord = tp_end->coord;
1904 gboolean done_start_end = FALSE;
1906 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1907 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1909 // This number can be configured via the settings if you really want to change it
1910 gdouble distance_diff;
1911 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1912 distance_diff = 100.0; // Metres
1914 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1915 // Start and end 'close' together so only draw one label at an average location
1916 gint x1, x2, y1, y2;
1917 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1918 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1920 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1922 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1923 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1926 done_start_end = TRUE;
1930 if ( ! done_start_end ) {
1931 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1932 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1933 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1934 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1935 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1936 g_free ( name_start );
1938 // Don't draw end label if this is the one being created
1939 if ( trk != dp->vtl->current_track ) {
1940 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1941 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1942 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1943 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1944 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1945 g_free ( name_end );
1950 g_free ( fgcolour );
1951 g_free ( bgcolour );
1955 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1957 if ( ! track->visible )
1960 /* TODO: this function is a mess, get rid of any redundancy */
1961 GList *list = track->trackpoints;
1963 gboolean useoldvals = TRUE;
1965 gboolean drawpoints;
1967 gboolean drawelevation;
1968 gdouble min_alt, max_alt, alt_diff = 0;
1970 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1971 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1974 if ( dp->vtl->drawelevation )
1976 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1977 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1978 alt_diff = max_alt - min_alt;
1981 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1982 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1983 trw_layer_draw_track ( id, track, dp, TRUE );
1985 if ( draw_track_outline )
1986 drawpoints = drawstops = FALSE;
1988 drawpoints = dp->vtl->drawpoints;
1989 drawstops = dp->vtl->drawstops;
1992 gboolean drawing_highlight = FALSE;
1993 /* Current track - used for creation */
1994 if ( track == dp->vtl->current_track )
1995 main_gc = dp->vtl->current_track_gc;
1997 if ( dp->highlight ) {
1998 /* Draw all tracks of the layer in special colour
1999 NB this supercedes the drawmode */
2000 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2001 drawing_highlight = TRUE;
2003 if ( !drawing_highlight ) {
2004 // Still need to figure out the gc according to the drawing mode:
2005 switch ( dp->vtl->drawmode ) {
2006 case DRAWMODE_BY_TRACK:
2007 if ( dp->vtl->track_1color_gc )
2008 g_object_unref ( dp->vtl->track_1color_gc );
2009 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2010 main_gc = dp->vtl->track_1color_gc;
2013 // Mostly for DRAWMODE_ALL_SAME_COLOR
2014 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2015 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2022 int x, y, oldx, oldy;
2023 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2025 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2027 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2029 // Draw the first point as something a bit different from the normal points
2030 // ATM it's slightly bigger and a triangle
2032 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2033 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2039 gdouble average_speed = 0.0;
2040 gdouble low_speed = 0.0;
2041 gdouble high_speed = 0.0;
2042 // If necessary calculate these values - which is done only once per track redraw
2043 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2044 // the percentage factor away from the average speed determines transistions between the levels
2045 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2046 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2047 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2050 while ((list = g_list_next(list)))
2052 tp = VIK_TRACKPOINT(list->data);
2053 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2055 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2056 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2057 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2058 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2059 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2061 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2064 * If points are the same in display coordinates, don't draw.
2066 if ( useoldvals && x == oldx && y == oldy )
2068 // Still need to process points to ensure 'stops' are drawn if required
2069 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2070 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2071 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 );
2076 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2077 if ( drawpoints || dp->vtl->drawlines ) {
2078 // setup main_gc for both point and line drawing
2079 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2080 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 ) );
2084 if ( drawpoints && ! draw_track_outline )
2089 * The concept of drawing stops is that a trackpoint
2090 * that is if the next trackpoint has a timestamp far into
2091 * the future, we draw a circle of 6x trackpoint size,
2092 * instead of a rectangle of 2x trackpoint size.
2093 * This is drawn first so the trackpoint will be drawn on top
2096 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2097 /* Stop point. Draw 6x circle. Always in redish colour */
2098 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 );
2100 /* Regular point - draw 2x square. */
2101 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2104 /* Final point - draw 4x circle. */
2105 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 );
2108 if ((!tp->newsegment) && (dp->vtl->drawlines))
2111 /* UTM only: zone check */
2112 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2113 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2116 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2118 if ( draw_track_outline ) {
2119 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2123 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2125 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2127 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2132 tmp[1].y = oldy-FIXALTITUDE(list->data);
2134 tmp[2].y = y-FIXALTITUDE(list->next->data);
2139 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2140 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2142 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2143 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2145 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2150 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2151 // Draw an arrow at the mid point to show the direction of the track
2152 // Code is a rework from vikwindow::draw_ruler()
2153 gint midx = (oldx + x) / 2;
2154 gint midy = (oldy + y) / 2;
2156 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2157 // Avoid divide by zero and ensure at least 1 pixel big
2159 gdouble dx = (oldx - midx) / len;
2160 gdouble dy = (oldy - midy) / len;
2161 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2162 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2172 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2174 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2175 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2177 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2179 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2180 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 ));
2184 * If points are the same in display coordinates, don't draw.
2186 if ( x != oldx || y != oldy )
2188 if ( draw_track_outline )
2189 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2191 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2197 * If points are the same in display coordinates, don't draw.
2199 if ( x != oldx && y != oldy )
2201 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2202 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2210 // Labels drawn after the trackpoints, so the labels are on top
2211 if ( dp->vtl->track_draw_labels ) {
2212 if ( track->max_number_dist_labels > 0 ) {
2213 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2216 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2217 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2223 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2225 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2226 trw_layer_draw_track ( id, track, dp, FALSE );
2230 static void cached_pixbuf_free ( CachedPixbuf *cp )
2232 g_object_unref ( G_OBJECT(cp->pixbuf) );
2233 g_free ( cp->image );
2236 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2238 return strcmp ( cp->image, name );
2241 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2244 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2245 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2246 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2249 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2251 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2253 if ( wp->image && dp->vtl->drawimages )
2255 GdkPixbuf *pixbuf = NULL;
2258 if ( dp->vtl->image_alpha == 0)
2261 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2263 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2266 gchar *image = wp->image;
2267 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2268 if ( ! regularthumb )
2270 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2271 image = "\x12\x00"; /* this shouldn't occur naturally. */
2275 CachedPixbuf *cp = NULL;
2276 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2277 if ( dp->vtl->image_size == 128 )
2278 cp->pixbuf = regularthumb;
2281 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2282 g_assert ( cp->pixbuf );
2283 g_object_unref ( G_OBJECT(regularthumb) );
2285 cp->image = g_strdup ( image );
2287 /* needed so 'click picture' tool knows how big the pic is; we don't
2288 * store it in cp because they may have been freed already. */
2289 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2290 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2292 g_queue_push_head ( dp->vtl->image_cache, cp );
2293 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2294 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2296 pixbuf = cp->pixbuf;
2300 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2306 w = gdk_pixbuf_get_width ( pixbuf );
2307 h = gdk_pixbuf_get_height ( pixbuf );
2309 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2311 if ( dp->highlight ) {
2312 // Highlighted - so draw a little border around the chosen one
2313 // single line seems a little weak so draw 2 of them
2314 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2315 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2316 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2317 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2320 if ( dp->vtl->image_alpha == 255 )
2321 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2323 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2325 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2329 // Draw appropriate symbol - either symbol image or simple types
2330 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2331 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 );
2333 else if ( wp == dp->vtl->current_wp ) {
2334 switch ( dp->vtl->wp_symbol ) {
2335 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;
2336 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;
2337 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;
2338 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 );
2339 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 );
2344 switch ( dp->vtl->wp_symbol ) {
2345 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;
2346 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;
2347 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;
2348 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 );
2349 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;
2354 if ( dp->vtl->drawlabels )
2356 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2357 gint label_x, label_y;
2359 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2361 // Could this stored in the waypoint rather than recreating each pass?
2362 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2364 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2365 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2367 // Fallback if parse failure
2368 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2370 g_free ( wp_label_markup );
2372 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2373 label_x = x - width/2;
2374 if ( wp->symbol_pixbuf )
2375 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2377 label_y = y - dp->vtl->wp_size - height - 2;
2379 /* if highlight mode on, then draw background text in highlight colour */
2380 if ( dp->highlight )
2381 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2383 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2384 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2389 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2391 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2392 trw_layer_draw_waypoint ( id, wp, dp );
2396 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2398 static struct DrawingParams dp;
2399 g_assert ( l != NULL );
2401 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2403 if ( l->tracks_visible )
2404 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2406 if ( l->routes_visible )
2407 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2409 if (l->waypoints_visible)
2410 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2413 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2415 // 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
2416 // This may seem slightly inefficient to test each time for every layer
2417 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2418 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2419 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2421 trw_layer_draw_with_highlight ( l, data, FALSE );
2424 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2426 // Check the layer for visibility (including all the parents visibilities)
2427 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2429 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2433 * vik_trw_layer_draw_highlight_item:
2435 * Only handles a single track or waypoint ATM
2436 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2438 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2440 // Check the layer for visibility (including all the parents visibilities)
2441 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2444 static struct DrawingParams dp;
2445 init_drawing_params ( &dp, vtl, vvp, TRUE );
2448 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2450 trw_layer_draw_track_cb ( NULL, trk, &dp );
2452 if ( vtl->waypoints_visible && wpt ) {
2453 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2458 * vik_trw_layer_draw_highlight_item:
2460 * Generally for drawing all tracks or routes or waypoints
2461 * trks may be actually routes
2462 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2464 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2466 // Check the layer for visibility (including all the parents visibilities)
2467 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2470 static struct DrawingParams dp;
2471 init_drawing_params ( &dp, vtl, vvp, TRUE );
2474 gboolean is_routes = (trks == vtl->routes);
2475 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2477 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2480 if ( vtl->waypoints_visible && wpts )
2481 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2484 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2487 if ( vtl->track_bg_gc )
2489 g_object_unref ( vtl->track_bg_gc );
2490 vtl->track_bg_gc = NULL;
2492 if ( vtl->track_1color_gc )
2494 g_object_unref ( vtl->track_1color_gc );
2495 vtl->track_1color_gc = NULL;
2497 if ( vtl->current_track_gc )
2499 g_object_unref ( vtl->current_track_gc );
2500 vtl->current_track_gc = NULL;
2502 if ( vtl->current_track_newpoint_gc )
2504 g_object_unref ( vtl->current_track_newpoint_gc );
2505 vtl->current_track_newpoint_gc = NULL;
2508 if ( ! vtl->track_gc )
2510 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2511 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2512 g_array_free ( vtl->track_gc, TRUE );
2513 vtl->track_gc = NULL;
2516 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2518 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2519 gint width = vtl->line_thickness;
2521 if ( vtl->track_gc )
2522 trw_layer_free_track_gcs ( vtl );
2524 if ( vtl->track_bg_gc )
2525 g_object_unref ( vtl->track_bg_gc );
2526 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2528 // Ensure new track drawing heeds line thickness setting
2529 // however always have a minium of 2, as 1 pixel is really narrow
2530 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2532 if ( vtl->current_track_gc )
2533 g_object_unref ( vtl->current_track_gc );
2534 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2535 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2537 // 'newpoint' gc is exactly the same as the current track gc
2538 if ( vtl->current_track_newpoint_gc )
2539 g_object_unref ( vtl->current_track_newpoint_gc );
2540 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2541 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2543 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2545 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2546 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2548 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2549 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2550 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2552 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2554 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2557 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2559 VikTrwLayer *rv = trw_layer_new1 ( vp );
2560 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2562 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2563 /* early exit, as the rest is GUI related */
2567 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2568 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2570 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2571 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2573 trw_layer_new_track_gcs ( rv, vp );
2575 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2576 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2577 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2578 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2580 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2582 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2587 #define SMALL_ICON_SIZE 18
2589 * Can accept a null symbol, and may return null value
2591 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2593 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2594 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2595 // So needing a small icon for the treeview may need some resizing:
2596 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2597 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2601 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2603 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2605 GdkPixbuf *pixbuf = NULL;
2607 if ( track->has_color ) {
2608 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2609 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2610 // Here is some magic found to do the conversion
2611 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2612 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2613 ((track->color.green & 0xff00) << 8) |
2614 (track->color.blue & 0xff00);
2616 gdk_pixbuf_fill ( pixbuf, pixel );
2619 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 );
2622 g_object_unref (pixbuf);
2624 *new_iter = *((GtkTreeIter *) pass_along[1]);
2625 if ( track->is_route )
2626 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2628 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2630 if ( ! track->visible )
2631 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2634 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2636 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2638 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 );
2640 *new_iter = *((GtkTreeIter *) pass_along[1]);
2641 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2643 if ( ! wp->visible )
2644 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2647 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2649 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2652 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2654 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2657 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2659 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2662 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2665 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2667 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2668 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2670 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2672 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2675 if ( g_hash_table_size (vtl->routes) > 0 ) {
2676 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2678 pass_along[0] = &(vtl->routes_iter);
2679 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2681 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2683 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2686 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2687 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2689 pass_along[0] = &(vtl->waypoints_iter);
2690 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2692 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2694 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2699 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2703 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2704 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2705 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2706 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2708 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2710 return (t->visible ^= 1);
2714 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2716 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2718 return (t->visible ^= 1);
2722 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2724 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2726 return (t->visible ^= 1);
2736 * Return a property about tracks for this layer
2738 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2740 return vtl->line_thickness;
2743 // Structure to hold multiple track information for a layer
2752 * Build up layer multiple track information via updating the tooltip_tracks structure
2754 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2756 tt->length = tt->length + vik_track_get_length (tr);
2758 // Ensure times are available
2759 if ( tr->trackpoints &&
2760 vik_track_get_tp_first(tr)->has_timestamp &&
2761 vik_track_get_tp_last(tr)->has_timestamp ) {
2764 t1 = vik_track_get_tp_first(tr)->timestamp;
2765 t2 = vik_track_get_tp_last(tr)->timestamp;
2767 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2768 // Hence initialize to the first 'proper' value
2769 if ( tt->start_time == 0 )
2770 tt->start_time = t1;
2771 if ( tt->end_time == 0 )
2774 // Update find the earliest / last times
2775 if ( t1 < tt->start_time )
2776 tt->start_time = t1;
2777 if ( t2 > tt->end_time )
2780 // Keep track of total time
2781 // there maybe gaps within a track (eg segments)
2782 // but this should be generally good enough for a simple indicator
2783 tt->duration = tt->duration + (int)(t2-t1);
2788 * Generate tooltip text for the layer.
2789 * This is relatively complicated as it considers information for
2790 * no tracks, a single track or multiple tracks
2791 * (which may or may not have timing information)
2793 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2804 static gchar tmp_buf[128];
2807 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2809 // Safety check - I think these should always be valid
2810 if ( vtl->tracks && vtl->waypoints ) {
2811 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2812 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2814 GDate* gdate_start = g_date_new ();
2815 g_date_set_time_t (gdate_start, tt.start_time);
2817 GDate* gdate_end = g_date_new ();
2818 g_date_set_time_t (gdate_end, tt.end_time);
2820 if ( g_date_compare (gdate_start, gdate_end) ) {
2821 // Dates differ so print range on separate line
2822 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2823 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2824 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2827 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2828 if ( tt.start_time != 0 )
2829 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2833 if ( tt.length > 0.0 ) {
2834 gdouble len_in_units;
2836 // Setup info dependent on distance units
2837 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2838 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2839 len_in_units = VIK_METERS_TO_MILES(tt.length);
2842 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2843 len_in_units = tt.length/1000.0;
2846 // Timing information if available
2848 if ( tt.duration > 0 ) {
2849 g_snprintf (tbuf1, sizeof(tbuf1),
2850 _(" in %d:%02d hrs:mins"),
2851 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2853 g_snprintf (tbuf2, sizeof(tbuf2),
2854 _("\n%sTotal Length %.1f %s%s"),
2855 tbuf3, len_in_units, tbuf4, tbuf1);
2858 // Put together all the elements to form compact tooltip text
2859 g_snprintf (tmp_buf, sizeof(tmp_buf),
2860 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2861 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2863 g_date_free (gdate_start);
2864 g_date_free (gdate_end);
2871 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2875 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2877 // Very simple tooltip - may expand detail in the future...
2878 static gchar tmp_buf[32];
2879 g_snprintf (tmp_buf, sizeof(tmp_buf),
2881 g_hash_table_size (l->tracks));
2885 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2887 // Very simple tooltip - may expand detail in the future...
2888 static gchar tmp_buf[32];
2889 g_snprintf (tmp_buf, sizeof(tmp_buf),
2891 g_hash_table_size (l->routes));
2896 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2897 // Same tooltip for a route
2898 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2901 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2902 tr = g_hash_table_lookup ( l->tracks, sublayer );
2904 tr = g_hash_table_lookup ( l->routes, sublayer );
2907 // Could be a better way of handling strings - but this works...
2908 gchar time_buf1[20];
2909 gchar time_buf2[20];
2910 time_buf1[0] = '\0';
2911 time_buf2[0] = '\0';
2912 static gchar tmp_buf[100];
2913 // Compact info: Short date eg (11/20/99), duration and length
2914 // Hopefully these are the things that are most useful and so promoted into the tooltip
2915 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2916 // %x The preferred date representation for the current locale without the time.
2917 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2918 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2919 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2921 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2924 // Get length and consider the appropriate distance units
2925 gdouble tr_len = vik_track_get_length(tr);
2926 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2927 switch (dist_units) {
2928 case VIK_UNITS_DISTANCE_KILOMETRES:
2929 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2931 case VIK_UNITS_DISTANCE_MILES:
2932 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2941 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2943 // Very simple tooltip - may expand detail in the future...
2944 static gchar tmp_buf[32];
2945 g_snprintf (tmp_buf, sizeof(tmp_buf),
2947 g_hash_table_size (l->waypoints));
2951 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2953 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2954 // NB It's OK to return NULL
2959 return w->description;
2968 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2971 * set_statusbar_msg_info_trkpt:
2973 * Function to show track point information on the statusbar
2974 * Items displayed is controlled by the settings format code
2976 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2978 gchar *statusbar_format_code = NULL;
2979 gboolean need2free = FALSE;
2980 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2981 // Otherwise use default
2982 statusbar_format_code = g_strdup ( "KEATDN" );
2986 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2987 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2991 g_free ( statusbar_format_code );
2995 * Function to show basic waypoint information on the statusbar
2997 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3000 switch (a_vik_get_units_height ()) {
3001 case VIK_UNITS_HEIGHT_FEET:
3002 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3005 //VIK_UNITS_HEIGHT_METRES:
3006 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3010 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3011 // one can easily use the current pointer position to see this if needed
3012 gchar *lat = NULL, *lon = NULL;
3013 static struct LatLon ll;
3014 vik_coord_to_latlon (&(wpt->coord), &ll);
3015 a_coords_latlon_to_string ( &ll, &lat, &lon );
3017 // Combine parts to make overall message
3020 // Add comment if available
3021 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3023 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3024 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3031 * General layer selection function, find out which bit is selected and take appropriate action
3033 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3036 l->current_wp = NULL;
3037 l->current_wp_id = NULL;
3038 trw_layer_cancel_current_tp ( l, FALSE );
3041 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3045 case VIK_TREEVIEW_TYPE_LAYER:
3047 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3048 /* Mark for redraw */
3053 case VIK_TREEVIEW_TYPE_SUBLAYER:
3057 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3059 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3060 /* Mark for redraw */
3064 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3066 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3067 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3068 /* Mark for redraw */
3072 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3074 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3075 /* Mark for redraw */
3079 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3081 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3082 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3083 /* Mark for redraw */
3087 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3089 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3090 /* Mark for redraw */
3094 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3096 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3098 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3099 // Show some waypoint info
3100 set_statusbar_msg_info_wpt ( l, wpt );
3101 /* Mark for redraw */
3108 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3117 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3122 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3127 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3132 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3134 return l->waypoints;
3137 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3139 return vtl->tracks_iters;
3142 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3144 return vtl->routes_iters;
3147 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3149 return vtl->waypoints;
3152 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3154 return ! ( g_hash_table_size ( vtl->tracks ) ||
3155 g_hash_table_size ( vtl->routes ) ||
3156 g_hash_table_size ( vtl->waypoints ) );
3159 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3161 return vtl->tracks_visible;
3164 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3166 return vtl->routes_visible;
3169 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3171 return vtl->waypoints_visible;
3175 * ATM use a case sensitive find
3176 * Finds the first one
3178 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3180 if ( wp && wp->name )
3181 if ( ! strcmp ( wp->name, name ) )
3187 * Get waypoint by name - not guaranteed to be unique
3188 * Finds the first one
3190 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3192 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3196 * ATM use a case sensitive find
3197 * Finds the first one
3199 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3201 if ( trk && trk->name )
3202 if ( ! strcmp ( trk->name, name ) )
3208 * Get track by name - not guaranteed to be unique
3209 * Finds the first one
3211 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3213 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3217 * Get route by name - not guaranteed to be unique
3218 * Finds the first one
3220 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3222 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3225 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3227 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3228 maxmin[0].lat = trk->bbox.north;
3229 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3230 maxmin[1].lat = trk->bbox.south;
3231 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3232 maxmin[0].lon = trk->bbox.east;
3233 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3234 maxmin[1].lon = trk->bbox.west;
3237 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3239 // Continually reuse maxmin to find the latest maximum and minimum values
3240 // First set to waypoints bounds
3241 maxmin[0].lat = vtl->waypoints_bbox.north;
3242 maxmin[1].lat = vtl->waypoints_bbox.south;
3243 maxmin[0].lon = vtl->waypoints_bbox.east;
3244 maxmin[1].lon = vtl->waypoints_bbox.west;
3245 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3246 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3249 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3251 /* 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... */
3252 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3253 trw_layer_find_maxmin (vtl, maxmin);
3254 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3258 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3259 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3264 static void trw_layer_centerize ( menu_array_layer values )
3266 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3268 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3269 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3271 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3274 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3276 /* First set the center [in case previously viewing from elsewhere] */
3277 /* Then loop through zoom levels until provided positions are in view */
3278 /* This method is not particularly fast - but should work well enough */
3279 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3281 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3282 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3284 /* Convert into definite 'smallest' and 'largest' positions */
3285 struct LatLon minmin;
3286 if ( maxmin[0].lat < maxmin[1].lat )
3287 minmin.lat = maxmin[0].lat;
3289 minmin.lat = maxmin[1].lat;
3291 struct LatLon maxmax;
3292 if ( maxmin[0].lon > maxmin[1].lon )
3293 maxmax.lon = maxmin[0].lon;
3295 maxmax.lon = maxmin[1].lon;
3297 /* Never zoom in too far - generally not that useful, as too close ! */
3298 /* Always recalculate the 'best' zoom level */
3300 vik_viewport_set_zoom ( vvp, zoom );
3302 gdouble min_lat, max_lat, min_lon, max_lon;
3303 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3304 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3305 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3306 /* NB I think the logic used in this test to determine if the bounds is within view
3307 fails if track goes across 180 degrees longitude.
3308 Hopefully that situation is not too common...
3309 Mind you viking doesn't really do edge locations to well anyway */
3310 if ( min_lat < minmin.lat &&
3311 max_lat > minmin.lat &&
3312 min_lon < maxmax.lon &&
3313 max_lon > maxmax.lon )
3314 /* Found within zoom level */
3319 vik_viewport_set_zoom ( vvp, zoom );
3323 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3325 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3326 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3327 trw_layer_find_maxmin (vtl, maxmin);
3328 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3331 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3336 static void trw_layer_auto_view ( menu_array_layer values )
3338 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3339 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3340 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3341 vik_layers_panel_emit_update ( vlp );
3344 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3347 static void trw_layer_export_gpspoint ( menu_array_layer values )
3349 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3351 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3353 g_free ( auto_save_name );
3356 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3358 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3360 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3362 g_free ( auto_save_name );
3365 static void trw_layer_export_gpx ( menu_array_layer values )
3367 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3369 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3371 g_free ( auto_save_name );
3374 static void trw_layer_export_kml ( menu_array_layer values )
3376 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3378 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3380 g_free ( auto_save_name );
3383 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3385 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3386 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3389 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3391 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3394 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3396 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3399 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3401 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3403 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3404 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3406 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3408 if ( !trk || !trk->name )
3411 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3413 gchar *label = NULL;
3414 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3415 label = _("Export Route as GPX");
3417 label = _("Export Track as GPX");
3418 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3420 g_free ( auto_save_name );
3423 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3425 wpu_udata *user_data = udata;
3426 if ( wp == user_data->wp ) {
3427 user_data->uuid = id;
3433 static void trw_layer_goto_wp ( menu_array_layer values )
3435 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3436 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3437 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3438 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3439 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3441 GTK_RESPONSE_REJECT,
3443 GTK_RESPONSE_ACCEPT,
3446 GtkWidget *label, *entry;
3447 label = gtk_label_new(_("Waypoint Name:"));
3448 entry = gtk_entry_new();
3450 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3451 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3452 gtk_widget_show_all ( label );
3453 gtk_widget_show_all ( entry );
3455 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3457 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3459 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3460 // Find *first* wp with the given name
3461 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3464 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3467 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3468 vik_layers_panel_emit_update ( vlp );
3470 // Find and select on the side panel
3475 // Hmmm, want key of it
3476 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3478 if ( wpf && udata.uuid ) {
3479 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3480 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3489 gtk_widget_destroy ( dia );
3492 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3494 gchar *default_name = highest_wp_number_get(vtl);
3495 VikWaypoint *wp = vik_waypoint_new();
3496 gchar *returned_name;
3498 wp->coord = *def_coord;
3500 // Attempt to auto set height if DEM data is available
3501 vik_waypoint_apply_dem_data ( wp, TRUE );
3503 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3505 if ( returned_name )
3508 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3509 g_free (default_name);
3510 g_free (returned_name);
3513 g_free (default_name);
3514 vik_waypoint_free(wp);
3518 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3520 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3521 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3522 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3523 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3524 VikViewport *vvp = vik_window_viewport(vw);
3526 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3527 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3528 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3529 trw_layer_calculate_bounds_waypoints ( vtl );
3530 vik_layers_panel_emit_update ( vlp );
3533 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3535 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3536 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3537 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3539 trw_layer_find_maxmin (vtl, maxmin);
3540 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3541 trw_layer_calculate_bounds_waypoints ( vtl );
3542 vik_layers_panel_emit_update ( vlp );
3545 #ifdef VIK_CONFIG_GEOTAG
3546 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3548 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3550 // Update directly - not changing the mtime
3551 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3554 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3556 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3559 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3563 * Use code in separate file for this feature as reasonably complex
3565 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3567 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3568 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3569 // Unset so can be reverified later if necessary
3570 vtl->has_verified_thumbnails = FALSE;
3572 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3578 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3580 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3581 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3583 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3589 static void trw_layer_geotagging ( menu_array_layer values )
3591 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3592 // Unset so can be reverified later if necessary
3593 vtl->has_verified_thumbnails = FALSE;
3595 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3602 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3604 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3606 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3607 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3608 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3609 VikViewport *vvp = vik_window_viewport(vw);
3611 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3615 * Acquire into this TRW Layer straight from GPS Device
3617 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3619 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3620 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3624 * Acquire into this TRW Layer from Directions
3626 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3628 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3632 * Acquire into this TRW Layer from an entered URL
3634 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3636 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3637 trw_layer_acquire ( values, &vik_datasource_url_interface );
3640 #ifdef VIK_CONFIG_OPENSTREETMAP
3642 * Acquire into this TRW Layer from OSM
3644 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3646 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3650 * Acquire into this TRW Layer from OSM for 'My' Traces
3652 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3654 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3658 #ifdef VIK_CONFIG_GEOCACHES
3660 * Acquire into this TRW Layer from Geocaching.com
3662 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3664 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3668 #ifdef VIK_CONFIG_GEOTAG
3670 * Acquire into this TRW Layer from images
3672 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3674 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3676 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3677 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3679 // Reverify thumbnails as they may have changed
3680 vtl->has_verified_thumbnails = FALSE;
3681 trw_layer_verify_thumbnails ( vtl, NULL );
3685 static void trw_layer_gps_upload ( menu_array_layer values )
3687 menu_array_sublayer data;
3689 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3691 data[MA_VTL] = values[MA_VTL];
3692 data[MA_VLP] = values[MA_VLP];
3694 trw_layer_gps_upload_any ( data );
3698 * If pass_along[3] is defined that this will upload just that track
3700 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3702 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3703 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3705 // May not actually get a track here as values[2&3] can be null
3706 VikTrack *track = NULL;
3707 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3708 gboolean xfer_all = FALSE;
3710 if ( values[MA_SUBTYPE] ) {
3712 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3713 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3716 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3717 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3720 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3723 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3727 else if ( !values[MA_CONFIRM] )
3728 xfer_all = TRUE; // i.e. whole layer
3730 if (track && !track->visible) {
3731 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3735 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3736 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3737 GTK_DIALOG_DESTROY_WITH_PARENT,
3739 GTK_RESPONSE_ACCEPT,
3741 GTK_RESPONSE_REJECT,
3744 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3745 GtkWidget *response_w = NULL;
3746 #if GTK_CHECK_VERSION (2, 20, 0)
3747 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3751 gtk_widget_grab_focus ( response_w );
3753 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3755 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3756 datasource_gps_clean_up ( dgs );
3757 gtk_widget_destroy ( dialog );
3761 // Get info from reused datasource dialog widgets
3762 gchar* protocol = datasource_gps_get_protocol ( dgs );
3763 gchar* port = datasource_gps_get_descriptor ( dgs );
3764 // NB don't free the above strings as they're references to values held elsewhere
3765 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3766 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3767 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3768 gboolean turn_off = datasource_gps_get_off ( dgs );
3770 gtk_widget_destroy ( dialog );
3772 // When called from the viewport - work the corresponding layerspanel:
3774 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3777 // Apply settings to transfer to the GPS device
3784 vik_layers_panel_get_viewport (vlp),
3793 * Acquire into this TRW Layer from any GPS Babel supported file
3795 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3797 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3798 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3799 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3800 VikViewport *vvp = vik_window_viewport(vw);
3802 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3805 static void trw_layer_new_wp ( menu_array_layer values )
3807 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3808 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3809 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3810 instead return true if you want to update. */
3811 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 ) {
3812 trw_layer_calculate_bounds_waypoints ( vtl );
3813 vik_layers_panel_emit_update ( vlp );
3817 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3819 vtl->current_track = vik_track_new();
3820 vik_track_set_defaults ( vtl->current_track );
3821 vtl->current_track->visible = TRUE;
3822 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3823 // Create track with the preferred colour from the layer properties
3824 vtl->current_track->color = vtl->track_color;
3826 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3827 vtl->current_track->has_color = TRUE;
3828 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3831 static void trw_layer_new_track ( menu_array_layer values )
3833 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3835 if ( ! vtl->current_track ) {
3836 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3837 new_track_create_common ( vtl, name );
3840 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3844 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3846 vtl->current_track = vik_track_new();
3847 vik_track_set_defaults ( vtl->current_track );
3848 vtl->current_track->visible = TRUE;
3849 vtl->current_track->is_route = TRUE;
3850 // By default make all routes red
3851 vtl->current_track->has_color = TRUE;
3852 gdk_color_parse ( "red", &vtl->current_track->color );
3853 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3856 static void trw_layer_new_route ( menu_array_layer values )
3858 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3860 if ( ! vtl->current_track ) {
3861 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3862 new_route_create_common ( vtl, name );
3864 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3868 static void trw_layer_auto_routes_view ( menu_array_layer values )
3870 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3871 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3873 if ( g_hash_table_size (vtl->routes) > 0 ) {
3874 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3875 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3876 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3877 vik_layers_panel_emit_update ( vlp );
3882 static void trw_layer_finish_track ( menu_array_layer values )
3884 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3885 vtl->current_track = NULL;
3886 vtl->route_finder_started = FALSE;
3887 vik_layer_emit_update ( VIK_LAYER(vtl) );
3890 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3892 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3893 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3895 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3896 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3897 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3898 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3899 vik_layers_panel_emit_update ( vlp );
3903 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3905 /* NB do not care if wp is visible or not */
3906 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3909 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3911 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3912 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3914 /* Only 1 waypoint - jump straight to it */
3915 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3916 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3917 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3919 /* If at least 2 waypoints - find center and then zoom to fit */
3920 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3922 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3923 maxmin[0].lat = vtl->waypoints_bbox.north;
3924 maxmin[1].lat = vtl->waypoints_bbox.south;
3925 maxmin[0].lon = vtl->waypoints_bbox.east;
3926 maxmin[1].lon = vtl->waypoints_bbox.west;
3927 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3930 vik_layers_panel_emit_update ( vlp );
3933 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3935 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3938 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3940 if ( values[MA_MISC] ) {
3941 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3942 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3946 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3948 static menu_array_layer pass_along;
3950 GtkWidget *export_submenu;
3951 pass_along[MA_VTL] = vtl;
3952 pass_along[MA_VLP] = vlp;
3954 item = gtk_menu_item_new();
3955 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3956 gtk_widget_show ( item );
3958 if ( vtl->current_track ) {
3959 if ( vtl->current_track->is_route )
3960 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3962 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3964 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3965 gtk_widget_show ( item );
3968 item = gtk_menu_item_new ();
3969 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3970 gtk_widget_show ( item );
3973 /* Now with icons */
3974 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3977 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3978 gtk_widget_show ( item );
3980 GtkWidget *view_submenu = gtk_menu_new();
3981 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3982 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3983 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3984 gtk_widget_show ( item );
3985 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3987 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3989 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3990 gtk_widget_show ( item );
3992 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3994 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3995 gtk_widget_show ( item );
3997 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3999 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4000 gtk_widget_show ( item );
4002 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4003 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4005 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4006 gtk_widget_show ( item );
4008 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4010 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4011 gtk_widget_show ( item );
4013 export_submenu = gtk_menu_new ();
4014 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4016 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4017 gtk_widget_show ( item );
4018 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4020 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4021 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4022 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4023 gtk_widget_show ( item );
4025 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4027 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4028 gtk_widget_show ( item );
4030 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4032 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4033 gtk_widget_show ( item );
4035 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4037 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4038 gtk_widget_show ( item );
4040 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4042 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4043 gtk_widget_show ( item );
4045 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4046 item = gtk_menu_item_new_with_mnemonic ( external1 );
4047 g_free ( external1 );
4048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4049 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4050 gtk_widget_show ( item );
4052 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4053 item = gtk_menu_item_new_with_mnemonic ( external2 );
4054 g_free ( external2 );
4055 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4056 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4057 gtk_widget_show ( item );
4059 GtkWidget *new_submenu = gtk_menu_new();
4060 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4061 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4062 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4063 gtk_widget_show(item);
4064 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4066 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4067 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4069 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4070 gtk_widget_show ( item );
4072 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4073 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4075 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4076 gtk_widget_show ( item );
4077 // Make it available only when a new track *not* already in progress
4078 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4080 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4081 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4083 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4084 gtk_widget_show ( item );
4085 // Make it available only when a new track *not* already in progress
4086 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4088 #ifdef VIK_CONFIG_GEOTAG
4089 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4092 gtk_widget_show ( item );
4095 GtkWidget *acquire_submenu = gtk_menu_new ();
4096 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4098 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4099 gtk_widget_show ( item );
4100 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4102 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4104 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4105 gtk_widget_show ( item );
4107 /* FIXME: only add menu when at least a routing engine has support for Directions */
4108 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4110 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4111 gtk_widget_show ( item );
4113 #ifdef VIK_CONFIG_OPENSTREETMAP
4114 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4117 gtk_widget_show ( item );
4119 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4121 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4122 gtk_widget_show ( item );
4125 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4127 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4128 gtk_widget_show ( item );
4130 #ifdef VIK_CONFIG_GEONAMES
4131 GtkWidget *wikipedia_submenu = gtk_menu_new();
4132 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4133 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4134 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4135 gtk_widget_show(item);
4136 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4138 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4141 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4142 gtk_widget_show ( item );
4144 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4145 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4147 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4148 gtk_widget_show ( item );
4151 #ifdef VIK_CONFIG_GEOCACHES
4152 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4155 gtk_widget_show ( item );
4158 #ifdef VIK_CONFIG_GEOTAG
4159 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4161 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4162 gtk_widget_show ( item );
4165 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4167 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4168 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4169 gtk_widget_show ( item );
4171 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4173 GtkWidget *upload_submenu = gtk_menu_new ();
4174 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4176 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4177 gtk_widget_show ( item );
4178 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4180 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4183 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4184 gtk_widget_show ( item );
4186 #ifdef VIK_CONFIG_OPENSTREETMAP
4187 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4188 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4190 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4191 gtk_widget_show ( item );
4194 GtkWidget *delete_submenu = gtk_menu_new ();
4195 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4196 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4197 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4198 gtk_widget_show ( item );
4199 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4201 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4204 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4205 gtk_widget_show ( item );
4207 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4210 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4211 gtk_widget_show ( item );
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4216 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4217 gtk_widget_show ( item );
4219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4222 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4223 gtk_widget_show ( item );
4225 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4228 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4229 gtk_widget_show ( item );
4231 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4232 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4233 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4234 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4235 gtk_widget_show ( item );
4237 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4238 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4240 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4241 gtk_widget_show ( item );
4244 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4245 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4247 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4248 gtk_widget_show ( item );
4251 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4255 gtk_widget_show ( item );
4256 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4258 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4262 gtk_widget_show ( item );
4263 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4266 // Fake Waypoint UUIDs vi simple increasing integer
4267 static guint wp_uuid = 0;
4269 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4273 vik_waypoint_set_name (wp, name);
4275 if ( VIK_LAYER(vtl)->realized )
4277 // Do we need to create the sublayer:
4278 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4279 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4282 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4284 // Visibility column always needed for waypoints
4285 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 );
4287 // Actual setting of visibility dependent on the waypoint
4288 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4290 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4292 // Sort now as post_read is not called on a realized waypoint
4293 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4296 highest_wp_number_add_wp(vtl, name);
4297 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4301 // Fake Track UUIDs vi simple increasing integer
4302 static guint tr_uuid = 0;
4304 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4308 vik_track_set_name (t, name);
4310 if ( VIK_LAYER(vtl)->realized )
4312 // Do we need to create the sublayer:
4313 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4314 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4317 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4318 // Visibility column always needed for tracks
4319 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 );
4321 // Actual setting of visibility dependent on the track
4322 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4324 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4326 // Sort now as post_read is not called on a realized track
4327 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4330 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4332 trw_layer_update_treeview ( vtl, t );
4335 // Fake Route UUIDs vi simple increasing integer
4336 static guint rt_uuid = 0;
4338 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4342 vik_track_set_name (t, name);
4344 if ( VIK_LAYER(vtl)->realized )
4346 // Do we need to create the sublayer:
4347 if ( g_hash_table_size (vtl->routes) == 0 ) {
4348 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4351 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4352 // Visibility column always needed for routes
4353 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 );
4354 // Actual setting of visibility dependent on the route
4355 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4357 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4359 // Sort now as post_read is not called on a realized route
4360 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4363 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4365 trw_layer_update_treeview ( vtl, t );
4368 /* to be called whenever a track has been deleted or may have been changed. */
4369 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4371 if (vtl->current_tp_track == trk )
4372 trw_layer_cancel_current_tp ( vtl, FALSE );
4376 * Normally this is done to due the waypoint size preference having changed
4378 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4380 GHashTableIter iter;
4381 gpointer key, value;
4384 g_hash_table_iter_init ( &iter, vtl->waypoints );
4385 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4386 VikWaypoint *wp = VIK_WAYPOINT(value);
4388 // Reapply symbol setting to update the pixbuf
4389 gchar *tmp_symbol = g_strdup ( wp->symbol );
4390 vik_waypoint_set_symbol ( wp, tmp_symbol );
4391 g_free ( tmp_symbol );
4397 * trw_layer_new_unique_sublayer_name:
4399 * Allocates a unique new name
4401 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4404 gchar *newname = g_strdup(name);
4409 switch ( sublayer_type ) {
4410 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4411 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4413 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4414 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4417 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4420 // If found a name already in use try adding 1 to it and we try again
4422 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4424 newname = new_newname;
4427 } while ( id != NULL);
4432 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4434 // No more uniqueness of name forced when loading from a file
4435 // This now makes this function a little redunant as we just flow the parameters through
4436 vik_trw_layer_add_waypoint ( vtl, name, wp );
4439 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4441 if ( vtl->route_finder_append && vtl->current_track ) {
4442 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4444 // enforce end of current track equal to start of tr
4445 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4446 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4447 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4448 vik_track_add_trackpoint ( vtl->current_track,
4449 vik_trackpoint_copy ( cur_end ),
4453 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4454 vik_track_free ( tr );
4455 vtl->route_finder_append = FALSE; /* this means we have added it */
4458 // No more uniqueness of name forced when loading from a file
4460 vik_trw_layer_add_route ( vtl, name, tr );
4462 vik_trw_layer_add_track ( vtl, name, tr );
4464 if ( vtl->route_finder_check_added_track ) {
4465 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4466 vtl->route_finder_added_track = tr;
4471 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4473 *l = g_list_append(*l, id);
4477 * Move an item from one TRW layer to another TRW layer
4479 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4481 // TODO reconsider strategy when moving within layer (if anything...)
4482 gboolean rename = ( vtl_src != vtl_dest );
4486 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4487 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4491 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4493 newname = g_strdup ( trk->name );
4495 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4496 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4498 vik_trw_layer_delete_track ( vtl_src, trk );
4501 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4502 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4506 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4508 newname = g_strdup ( trk->name );
4510 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4511 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4513 vik_trw_layer_delete_route ( vtl_src, trk );
4516 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4517 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4521 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4523 newname = g_strdup ( wp->name );
4525 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4526 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4528 trw_layer_delete_waypoint ( vtl_src, wp );
4530 // Recalculate bounds even if not renamed as maybe dragged between layers
4531 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4532 trw_layer_calculate_bounds_waypoints ( vtl_src );
4536 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4538 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4539 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4541 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4542 GList *items = NULL;
4545 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4546 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4548 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4549 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4551 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4552 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4557 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4558 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4559 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4560 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4562 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4569 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4570 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4574 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4576 trku_udata *user_data = udata;
4577 if ( trk == user_data->trk ) {
4578 user_data->uuid = id;
4584 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4586 gboolean was_visible = FALSE;
4587 if ( trk && trk->name ) {
4589 if ( trk == vtl->current_track ) {
4590 vtl->current_track = NULL;
4591 vtl->current_tp_track = NULL;
4592 vtl->current_tp_id = NULL;
4593 vtl->moving_tp = FALSE;
4594 vtl->route_finder_started = FALSE;
4597 was_visible = trk->visible;
4599 if ( trk == vtl->route_finder_added_track )
4600 vtl->route_finder_added_track = NULL;
4606 // Hmmm, want key of it
4607 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4609 if ( trkf && udata.uuid ) {
4610 /* could be current_tp, so we have to check */
4611 trw_layer_cancel_tps_of_track ( vtl, trk );
4613 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4616 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4617 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4618 g_hash_table_remove ( vtl->tracks, udata.uuid );
4620 // If last sublayer, then remove sublayer container
4621 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4622 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4625 // Incase it was selected (no item delete signal ATM)
4626 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4632 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4634 gboolean was_visible = FALSE;
4636 if ( trk && trk->name ) {
4638 if ( trk == vtl->current_track ) {
4639 vtl->current_track = NULL;
4640 vtl->current_tp_track = NULL;
4641 vtl->current_tp_id = NULL;
4642 vtl->moving_tp = FALSE;
4645 was_visible = trk->visible;
4647 if ( trk == vtl->route_finder_added_track )
4648 vtl->route_finder_added_track = NULL;
4654 // Hmmm, want key of it
4655 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4657 if ( trkf && udata.uuid ) {
4658 /* could be current_tp, so we have to check */
4659 trw_layer_cancel_tps_of_track ( vtl, trk );
4661 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4664 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4665 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4666 g_hash_table_remove ( vtl->routes, udata.uuid );
4668 // If last sublayer, then remove sublayer container
4669 if ( g_hash_table_size (vtl->routes) == 0 ) {
4670 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4673 // Incase it was selected (no item delete signal ATM)
4674 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4680 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4682 gboolean was_visible = FALSE;
4684 if ( wp && wp->name ) {
4686 if ( wp == vtl->current_wp ) {
4687 vtl->current_wp = NULL;
4688 vtl->current_wp_id = NULL;
4689 vtl->moving_wp = FALSE;
4692 was_visible = wp->visible;
4698 // Hmmm, want key of it
4699 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4701 if ( wpf && udata.uuid ) {
4702 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4705 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4706 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4708 highest_wp_number_remove_wp(vtl, wp->name);
4709 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4711 // If last sublayer, then remove sublayer container
4712 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4713 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4716 // Incase it was selected (no item delete signal ATM)
4717 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4725 // Only for temporary use by trw_layer_delete_waypoint_by_name
4726 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4728 wpu_udata *user_data = udata;
4729 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4730 user_data->uuid = id;
4737 * Delete a waypoint by the given name
4738 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4739 * as there be multiple waypoints with the same name
4741 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4744 // Fake a waypoint with the given name
4745 udata.wp = vik_waypoint_new ();
4746 vik_waypoint_set_name (udata.wp, name);
4747 // Currently only the name is used in this waypoint find function
4750 // Hmmm, want key of it
4751 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4753 vik_waypoint_free (udata.wp);
4755 if ( wpf && udata.uuid )
4756 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4762 VikTrack *trk; // input
4763 gpointer uuid; // output
4766 // Only for temporary use by trw_layer_delete_track_by_name
4767 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4769 tpu_udata *user_data = udata;
4770 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4771 user_data->uuid = id;
4778 * Delete a track by the given name
4779 * NOTE: ATM this will delete the first encountered Track with the specified name
4780 * as there may be multiple tracks with the same name within the specified hash table
4782 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4785 // Fake a track with the given name
4786 udata.trk = vik_track_new ();
4787 vik_track_set_name (udata.trk, name);
4788 // Currently only the name is used in this waypoint find function
4791 // Hmmm, want key of it
4792 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4794 vik_track_free (udata.trk);
4796 if ( trkf && udata.uuid ) {
4797 // This could be a little better written...
4798 if ( vtl->tracks == ht_tracks )
4799 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4800 if ( vtl->routes == ht_tracks )
4801 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4808 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4810 vik_treeview_item_delete (vt, it );
4813 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4816 vtl->current_track = NULL;
4817 vtl->route_finder_added_track = NULL;
4818 if (vtl->current_tp_track)
4819 trw_layer_cancel_current_tp(vtl, FALSE);
4821 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4822 g_hash_table_remove_all(vtl->routes_iters);
4823 g_hash_table_remove_all(vtl->routes);
4825 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4827 vik_layer_emit_update ( VIK_LAYER(vtl) );
4830 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4833 vtl->current_track = NULL;
4834 vtl->route_finder_added_track = NULL;
4835 if (vtl->current_tp_track)
4836 trw_layer_cancel_current_tp(vtl, FALSE);
4838 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4839 g_hash_table_remove_all(vtl->tracks_iters);
4840 g_hash_table_remove_all(vtl->tracks);
4842 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4844 vik_layer_emit_update ( VIK_LAYER(vtl) );
4847 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4849 vtl->current_wp = NULL;
4850 vtl->current_wp_id = NULL;
4851 vtl->moving_wp = FALSE;
4853 highest_wp_number_reset(vtl);
4855 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4856 g_hash_table_remove_all(vtl->waypoints_iters);
4857 g_hash_table_remove_all(vtl->waypoints);
4859 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4861 vik_layer_emit_update ( VIK_LAYER(vtl) );
4864 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4866 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4867 // Get confirmation from the user
4868 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4869 _("Are you sure you want to delete all tracks in %s?"),
4870 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4871 vik_trw_layer_delete_all_tracks (vtl);
4874 static void trw_layer_delete_all_routes ( menu_array_layer values )
4876 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4877 // Get confirmation from the user
4878 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4879 _("Are you sure you want to delete all routes in %s?"),
4880 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4881 vik_trw_layer_delete_all_routes (vtl);
4884 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4886 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4887 // Get confirmation from the user
4888 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4889 _("Are you sure you want to delete all waypoints in %s?"),
4890 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4891 vik_trw_layer_delete_all_waypoints (vtl);
4894 static void trw_layer_delete_item ( menu_array_sublayer values )
4896 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4897 gboolean was_visible = FALSE;
4898 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4900 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4901 if ( wp && wp->name ) {
4902 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4903 // Get confirmation from the user
4904 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4905 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4906 _("Are you sure you want to delete the waypoint \"%s\"?"),
4909 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4912 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4914 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4915 if ( trk && trk->name ) {
4916 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4917 // Get confirmation from the user
4918 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4919 _("Are you sure you want to delete the track \"%s\"?"),
4922 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4927 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4928 if ( trk && trk->name ) {
4929 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4930 // Get confirmation from the user
4931 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4932 _("Are you sure you want to delete the route \"%s\"?"),
4935 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4939 vik_layer_emit_update ( VIK_LAYER(vtl) );
4943 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4945 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4947 vik_waypoint_set_name ( wp, new_name );
4949 // Now update the treeview as well
4954 // Need key of it for treeview update
4955 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4957 if ( wpf && udataU.uuid ) {
4958 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4961 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4962 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4968 * Maintain icon of waypoint in the treeview
4970 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4972 // update the treeview
4977 // Need key of it for treeview update
4978 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4980 if ( wpf && udataU.uuid ) {
4981 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4984 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4989 static void trw_layer_properties_item ( menu_array_sublayer values )
4991 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4992 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4994 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4996 if ( wp && wp->name )
4998 gboolean updated = FALSE;
4999 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5001 trw_layer_waypoint_rename ( vtl, wp, new_name );
5003 if ( updated && values[MA_TV_ITER] )
5004 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5006 if ( updated && VIK_LAYER(vtl)->visible )
5007 vik_layer_emit_update ( VIK_LAYER(vtl) );
5013 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5014 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5016 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5018 if ( tr && tr->name )
5020 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5031 * trw_layer_track_statistics:
5033 * Show track statistics.
5034 * ATM jump to the stats page in the properties
5035 * TODO: consider separating the stats into an individual dialog?
5037 static void trw_layer_track_statistics ( menu_array_sublayer values )
5039 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5041 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5042 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5044 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5046 if ( trk && trk->name ) {
5047 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5057 * Update the treeview of the track id - primarily to update the icon
5059 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5065 gpointer *trkf = NULL;
5066 if ( trk->is_route )
5067 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5069 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5071 if ( trkf && udata.uuid ) {
5073 GtkTreeIter *iter = NULL;
5074 if ( trk->is_route )
5075 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5077 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5080 // TODO: Make this a function
5081 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5082 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5083 ((trk->color.green & 0xff00) << 8) |
5084 (trk->color.blue & 0xff00);
5085 gdk_pixbuf_fill ( pixbuf, pixel );
5086 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5087 g_object_unref (pixbuf);
5094 Parameter 1 -> VikLayersPanel
5095 Parameter 2 -> VikLayer
5096 Parameter 3 -> VikViewport
5098 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5101 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5102 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5105 /* since vlp not set, vl & vvp should be valid instead! */
5107 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5108 vik_layer_emit_update ( VIK_LAYER(vl) );
5113 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5115 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5117 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5118 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5120 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5122 if ( track && track->trackpoints )
5123 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5126 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5128 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5130 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5131 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5133 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5135 if ( track && track->trackpoints )
5137 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5139 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5140 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5141 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5142 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5143 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5147 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5149 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5151 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5152 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5154 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5159 // Converting a track to a route can be a bit more complicated,
5160 // so give a chance to change our minds:
5161 if ( !trk->is_route &&
5162 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5163 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5165 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5166 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5171 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5174 trk_copy->is_route = !trk_copy->is_route;
5176 // ATM can't set name to self - so must create temporary copy
5177 gchar *name = g_strdup ( trk_copy->name );
5179 // Delete old one and then add new one
5180 if ( trk->is_route ) {
5181 vik_trw_layer_delete_route ( vtl, trk );
5182 vik_trw_layer_add_track ( vtl, name, trk_copy );
5185 // Extra route conversion bits...
5186 vik_track_merge_segments ( trk_copy );
5187 vik_track_to_routepoints ( trk_copy );
5189 vik_trw_layer_delete_track ( vtl, trk );
5190 vik_trw_layer_add_route ( vtl, name, trk_copy );
5194 // Update in case color of track / route changes when moving between sublayers
5195 vik_layer_emit_update ( VIK_LAYER(vtl) );
5198 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5200 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5202 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5203 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5205 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5208 vik_track_anonymize_times ( track );
5211 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5213 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5215 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5216 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5218 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5223 vtl->current_track = track;
5224 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);
5226 if ( track->trackpoints )
5227 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5231 * extend a track using route finder
5233 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5235 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5236 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5240 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5241 vtl->current_track = track;
5242 vtl->route_finder_started = TRUE;
5244 if ( track->trackpoints )
5245 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5251 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5253 // If have a vlp then perform a basic test to see if any DEM info available...
5255 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5257 if ( !g_list_length(dems) ) {
5258 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5266 * apply_dem_data_common:
5268 * A common function for applying the DEM values and reporting the results.
5270 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5272 if ( !trw_layer_dem_test ( vtl, vlp ) )
5275 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5276 // Inform user how much was changed
5278 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5279 g_snprintf(str, 64, tmp_str, changed);
5280 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5283 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5285 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5287 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5288 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5290 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5293 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5296 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5298 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5300 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5301 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5303 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5306 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5312 * A common function for applying the elevation smoothing and reporting the results.
5314 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5316 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5317 // Inform user how much was changed
5319 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5320 g_snprintf(str, 64, tmp_str, changed);
5321 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5327 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5329 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5331 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5332 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5334 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5339 smooth_it ( vtl, track, FALSE );
5342 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5344 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5346 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5347 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5349 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5354 smooth_it ( vtl, track, TRUE );
5358 * Commonal helper function
5360 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5363 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5364 g_snprintf(str, 64, tmp_str, changed);
5365 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5368 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5370 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5371 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5373 if ( !trw_layer_dem_test ( vtl, vlp ) )
5377 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5379 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5381 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5385 GHashTableIter iter;
5386 gpointer key, value;
5388 g_hash_table_iter_init ( &iter, vtl->waypoints );
5389 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5390 VikWaypoint *wp = VIK_WAYPOINT(value);
5391 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5394 wp_changed_message ( vtl, changed );
5397 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5399 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5400 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5402 if ( !trw_layer_dem_test ( vtl, vlp ) )
5406 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5408 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5410 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5414 GHashTableIter iter;
5415 gpointer key, value;
5417 g_hash_table_iter_init ( &iter, vtl->waypoints );
5418 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5419 VikWaypoint *wp = VIK_WAYPOINT(value);
5420 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5423 wp_changed_message ( vtl, changed );
5426 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5428 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5430 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5431 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5433 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5437 if ( !track->trackpoints )
5439 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5442 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5444 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5446 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5447 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5449 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5454 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5457 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5460 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5462 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5464 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5472 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5475 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5478 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5480 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5482 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5483 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5485 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5490 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5493 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5497 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5499 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5501 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5503 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5504 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5506 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5508 if ( trk && trk->trackpoints )
5510 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5511 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5512 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5513 if ( values[MA_VLP] )
5514 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5516 vik_layer_emit_update ( VIK_LAYER(vtl) );
5521 * Refine the selected track/route with a routing engine.
5522 * The routing engine is selected by the user, when requestiong the job.
5524 static void trw_layer_route_refine ( menu_array_sublayer values )
5526 static gint last_engine = 0;
5527 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5530 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5531 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5533 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5535 if ( trk && trk->trackpoints )
5537 /* Check size of the route */
5538 int nb = vik_track_get_tp_count(trk);
5540 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5541 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5542 GTK_MESSAGE_WARNING,
5543 GTK_BUTTONS_OK_CANCEL,
5544 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5546 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5547 gtk_widget_destroy ( dialog );
5548 if (response != GTK_RESPONSE_OK )
5551 /* Select engine from dialog */
5552 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5553 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5554 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5556 GTK_RESPONSE_REJECT,
5558 GTK_RESPONSE_ACCEPT,
5560 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5561 gtk_widget_show_all(label);
5563 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5565 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5566 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5567 gtk_widget_show_all(combo);
5569 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5571 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5573 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5575 /* Dialog validated: retrieve selected engine and do the job */
5576 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5577 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5580 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5582 /* Force saving track */
5583 /* FIXME: remove or rename this hack */
5584 vtl->route_finder_check_added_track = TRUE;
5587 vik_routing_engine_refine (routing, vtl, trk);
5589 /* FIXME: remove or rename this hack */
5590 if ( vtl->route_finder_added_track )
5591 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5593 vtl->route_finder_added_track = NULL;
5594 vtl->route_finder_check_added_track = FALSE;
5596 vik_layer_emit_update ( VIK_LAYER(vtl) );
5598 /* Restore cursor */
5599 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5601 gtk_widget_destroy ( dialog );
5605 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5607 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5608 trw_layer_tpwin_init ( vtl );
5611 /*************************************
5612 * merge/split by time routines
5613 *************************************/
5615 /* called for each key in track hash table.
5616 * If the current track has the same time stamp type, add it to the result,
5617 * except the one pointed by "exclude".
5618 * set exclude to NULL if there is no exclude to check.
5619 * Note that the result is in reverse (for performance reasons).
5624 gboolean with_timestamps;
5626 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5628 twt_udata *user_data = udata;
5629 VikTrackpoint *p1, *p2;
5630 VikTrack *trk = VIK_TRACK(value);
5631 if (trk == user_data->exclude) {
5635 if (trk->trackpoints) {
5636 p1 = vik_track_get_tp_first(trk);
5637 p2 = vik_track_get_tp_last(trk);
5639 if ( user_data->with_timestamps ) {
5640 if (!p1->has_timestamp || !p2->has_timestamp) {
5645 // Don't add tracks with timestamps when getting non timestamp tracks
5646 if (p1->has_timestamp || p2->has_timestamp) {
5652 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5655 /* called for each key in track hash table. if original track user_data[1] is close enough
5656 * to the passed one, add it to list in user_data[0]
5658 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5661 VikTrackpoint *p1, *p2;
5662 VikTrack *trk = VIK_TRACK(value);
5664 GList **nearby_tracks = ((gpointer *)user_data)[0];
5667 * detect reasons for not merging, and return
5668 * if no reason is found not to merge, then do it.
5671 twt_udata *udata = user_data;
5672 // Exclude the original track from the compiled list
5673 if (trk == udata->exclude) {
5677 t1 = vik_track_get_tp_first(trk)->timestamp;
5678 t2 = vik_track_get_tp_last(trk)->timestamp;
5680 if (trk->trackpoints) {
5681 p1 = vik_track_get_tp_first(trk);
5682 p2 = vik_track_get_tp_last(trk);
5684 if (!p1->has_timestamp || !p2->has_timestamp) {
5685 //g_print("no timestamp\n");
5689 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5690 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5691 if (! (abs(t1 - p2->timestamp) < threshold ||
5693 abs(p1->timestamp - t2) < threshold)
5700 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5703 /* comparison function used to sort tracks; a and b are hash table keys */
5704 /* Not actively used - can be restored if needed
5705 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5707 GHashTable *tracks = user_data;
5710 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5711 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5713 if (t1 < t2) return -1;
5714 if (t1 > t2) return 1;
5719 /* comparison function used to sort trackpoints */
5720 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5722 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5724 if (t1 < t2) return -1;
5725 if (t1 > t2) return 1;
5730 * comparison function which can be used to sort tracks or waypoints by name
5732 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5734 const gchar* namea = (const gchar*) a;
5735 const gchar* nameb = (const gchar*) b;
5736 if ( namea == NULL || nameb == NULL)
5739 // Same sort method as used in the vik_treeview_*_alphabetize functions
5740 return strcmp ( namea, nameb );
5744 * Attempt to merge selected track with other tracks specified by the user
5745 * Tracks to merge with must be of the same 'type' as the selected track -
5746 * either all with timestamps, or all without timestamps
5748 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5750 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5751 GList *other_tracks = NULL;
5752 GHashTable *ght_tracks;
5753 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5754 ght_tracks = vtl->routes;
5756 ght_tracks = vtl->tracks;
5758 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5763 if ( !track->trackpoints )
5767 udata.result = &other_tracks;
5768 udata.exclude = track;
5769 // Allow merging with 'similar' time type time tracks
5770 // i.e. either those times, or those without
5771 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5773 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5774 other_tracks = g_list_reverse(other_tracks);
5776 if ( !other_tracks ) {
5777 if ( udata.with_timestamps )
5778 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5780 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5784 // Sort alphabetically for user presentation
5785 // Convert into list of names for usage with dialog function
5786 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5787 GList *other_tracks_names = NULL;
5788 GList *iter = g_list_first ( other_tracks );
5790 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5791 iter = g_list_next ( iter );
5794 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5796 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5800 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5801 g_list_free(other_tracks);
5802 g_list_free(other_tracks_names);
5807 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5808 VikTrack *merge_track;
5809 if ( track->is_route )
5810 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5812 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5815 vik_track_steal_and_append_trackpoints ( track, merge_track );
5816 if ( track->is_route )
5817 vik_trw_layer_delete_route (vtl, merge_track);
5819 vik_trw_layer_delete_track (vtl, merge_track);
5820 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5823 for (l = merge_list; l != NULL; l = g_list_next(l))
5825 g_list_free(merge_list);
5827 vik_layer_emit_update( VIK_LAYER(vtl) );
5831 // c.f. trw_layer_sorted_track_id_by_name_list
5832 // but don't add the specified track to the list (normally current track)
5833 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5835 twt_udata *user_data = udata;
5838 if (trk == user_data->exclude) {
5842 // Sort named list alphabetically
5843 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5847 * Join - this allows combining 'tracks' and 'track routes'
5848 * i.e. doesn't care about whether tracks have consistent timestamps
5849 * ATM can only append one track at a time to the currently selected track
5851 static void trw_layer_append_track ( menu_array_sublayer values )
5854 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5856 GHashTable *ght_tracks;
5857 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5858 ght_tracks = vtl->routes;
5860 ght_tracks = vtl->tracks;
5862 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5867 GList *other_tracks_names = NULL;
5869 // Sort alphabetically for user presentation
5870 // Convert into list of names for usage with dialog function
5871 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5873 udata.result = &other_tracks_names;
5874 udata.exclude = trk;
5876 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5878 // Note the limit to selecting one track only
5879 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5880 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5881 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5884 trk->is_route ? _("Append Route"): _("Append Track"),
5885 trk->is_route ? _("Select the route to append after the current route") :
5886 _("Select the track to append after the current track") );
5888 g_list_free(other_tracks_names);
5890 // It's a list, but shouldn't contain more than one other track!
5891 if ( append_list ) {
5893 for (l = append_list; l != NULL; l = g_list_next(l)) {
5894 // TODO: at present this uses the first track found by name,
5895 // which with potential multiple same named tracks may not be the one selected...
5896 VikTrack *append_track;
5897 if ( trk->is_route )
5898 append_track = vik_trw_layer_get_route ( vtl, l->data );
5900 append_track = vik_trw_layer_get_track ( vtl, l->data );
5902 if ( append_track ) {
5903 vik_track_steal_and_append_trackpoints ( trk, append_track );
5904 if ( trk->is_route )
5905 vik_trw_layer_delete_route (vtl, append_track);
5907 vik_trw_layer_delete_track (vtl, append_track);
5910 for (l = append_list; l != NULL; l = g_list_next(l))
5912 g_list_free(append_list);
5914 vik_layer_emit_update( VIK_LAYER(vtl) );
5919 * Very similar to trw_layer_append_track for joining
5920 * but this allows selection from the 'other' list
5921 * If a track is selected, then is shows routes and joins the selected one
5922 * If a route is selected, then is shows tracks and joins the selected one
5924 static void trw_layer_append_other ( menu_array_sublayer values )
5927 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5929 GHashTable *ght_mykind, *ght_others;
5930 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5931 ght_mykind = vtl->routes;
5932 ght_others = vtl->tracks;
5935 ght_mykind = vtl->tracks;
5936 ght_others = vtl->routes;
5939 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5944 GList *other_tracks_names = NULL;
5946 // Sort alphabetically for user presentation
5947 // Convert into list of names for usage with dialog function
5948 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5950 udata.result = &other_tracks_names;
5951 udata.exclude = trk;
5953 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5955 // Note the limit to selecting one track only
5956 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5957 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5958 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5961 trk->is_route ? _("Append Track"): _("Append Route"),
5962 trk->is_route ? _("Select the track to append after the current route") :
5963 _("Select the route to append after the current track") );
5965 g_list_free(other_tracks_names);
5967 // It's a list, but shouldn't contain more than one other track!
5968 if ( append_list ) {
5970 for (l = append_list; l != NULL; l = g_list_next(l)) {
5971 // TODO: at present this uses the first track found by name,
5972 // which with potential multiple same named tracks may not be the one selected...
5974 // Get FROM THE OTHER TYPE list
5975 VikTrack *append_track;
5976 if ( trk->is_route )
5977 append_track = vik_trw_layer_get_track ( vtl, l->data );
5979 append_track = vik_trw_layer_get_route ( vtl, l->data );
5981 if ( append_track ) {
5983 if ( !append_track->is_route &&
5984 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5985 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5987 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5988 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5989 vik_track_merge_segments ( append_track );
5990 vik_track_to_routepoints ( append_track );
5997 vik_track_steal_and_append_trackpoints ( trk, append_track );
5999 // Delete copied which is FROM THE OTHER TYPE list
6000 if ( trk->is_route )
6001 vik_trw_layer_delete_track (vtl, append_track);
6003 vik_trw_layer_delete_route (vtl, append_track);
6006 for (l = append_list; l != NULL; l = g_list_next(l))
6008 g_list_free(append_list);
6009 vik_layer_emit_update( VIK_LAYER(vtl) );
6013 /* merge by segments */
6014 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6016 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6017 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6018 guint segments = vik_track_merge_segments ( trk );
6019 // NB currently no need to redraw as segments not actually shown on the display
6020 // However inform the user of what happened:
6022 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6023 g_snprintf(str, 64, tmp_str, segments);
6024 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6027 /* merge by time routine */
6028 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6030 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6034 GList *tracks_with_timestamp = NULL;
6035 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6036 if (orig_trk->trackpoints &&
6037 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6038 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6043 udata.result = &tracks_with_timestamp;
6044 udata.exclude = orig_trk;
6045 udata.with_timestamps = TRUE;
6046 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6047 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6049 if (!tracks_with_timestamp) {
6050 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6053 g_list_free(tracks_with_timestamp);
6055 static guint threshold_in_minutes = 1;
6056 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6057 _("Merge Threshold..."),
6058 _("Merge when time between tracks less than:"),
6059 &threshold_in_minutes)) {
6063 // keep attempting to merge all tracks until no merges within the time specified is possible
6064 gboolean attempt_merge = TRUE;
6065 GList *nearby_tracks = NULL;
6067 static gpointer params[3];
6069 while ( attempt_merge ) {
6071 // Don't try again unless tracks have changed
6072 attempt_merge = FALSE;
6074 trps = orig_trk->trackpoints;
6078 if (nearby_tracks) {
6079 g_list_free(nearby_tracks);
6080 nearby_tracks = NULL;
6083 params[0] = &nearby_tracks;
6084 params[1] = (gpointer)trps;
6085 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6087 /* get a list of adjacent-in-time tracks */
6088 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6091 GList *l = nearby_tracks;
6093 /* remove trackpoints from merged track, delete track */
6094 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6095 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6097 // Tracks have changed, therefore retry again against all the remaining tracks
6098 attempt_merge = TRUE;
6103 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6106 g_list_free(nearby_tracks);
6108 vik_layer_emit_update( VIK_LAYER(vtl) );
6112 * Split a track at the currently selected trackpoint
6114 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6116 if ( !vtl->current_tpl )
6119 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6120 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6122 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6123 GList *newglist = g_list_alloc ();
6124 newglist->prev = NULL;
6125 newglist->next = vtl->current_tpl->next;
6126 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6127 tr->trackpoints = newglist;
6129 vtl->current_tpl->next->prev = newglist; /* end old track here */
6130 vtl->current_tpl->next = NULL;
6132 // Bounds of the selected track changed due to the split
6133 vik_track_calculate_bounds ( vtl->current_tp_track );
6135 vtl->current_tpl = newglist; /* change tp to first of new track. */
6136 vtl->current_tp_track = tr;
6139 vik_trw_layer_add_route ( vtl, name, tr );
6141 vik_trw_layer_add_track ( vtl, name, tr );
6143 // Bounds of the new track created by the split
6144 vik_track_calculate_bounds ( tr );
6150 // Also need id of newly created track
6153 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6155 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6157 if ( trkf && udata.uuid )
6158 vtl->current_tp_id = udata.uuid;
6160 vtl->current_tp_id = NULL;
6162 vik_layer_emit_update(VIK_LAYER(vtl));
6168 /* split by time routine */
6169 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6171 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6172 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6173 GList *trps = track->trackpoints;
6175 GList *newlists = NULL;
6176 GList *newtps = NULL;
6177 static guint thr = 1;
6184 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6185 _("Split Threshold..."),
6186 _("Split when time between trackpoints exceeds:"),
6191 /* iterate through trackpoints, and copy them into new lists without touching original list */
6192 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6196 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6198 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6201 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6202 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6203 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6205 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6210 if (ts - prev_ts > thr*60) {
6211 /* flush accumulated trackpoints into new list */
6212 newlists = g_list_append(newlists, g_list_reverse(newtps));
6216 /* accumulate trackpoint copies in newtps, in reverse order */
6217 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6219 iter = g_list_next(iter);
6222 newlists = g_list_append(newlists, g_list_reverse(newtps));
6225 /* put lists of trackpoints into tracks */
6227 // Only bother updating if the split results in new tracks
6228 if (g_list_length (newlists) > 1) {
6233 tr = vik_track_copy ( track, FALSE );
6234 tr->trackpoints = (GList *)(iter->data);
6236 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6237 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6238 g_free ( new_tr_name );
6239 vik_track_calculate_bounds ( tr );
6240 iter = g_list_next(iter);
6242 // Remove original track and then update the display
6243 vik_trw_layer_delete_track (vtl, track);
6244 vik_layer_emit_update(VIK_LAYER(vtl));
6246 g_list_free(newlists);
6250 * Split a track by the number of points as specified by the user
6252 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6254 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6256 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6257 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6259 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6264 // Check valid track
6265 GList *trps = track->trackpoints;
6269 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6270 _("Split Every Nth Point"),
6271 _("Split on every Nth point:"),
6272 250, // Default value as per typical limited track capacity of various GPS devices
6276 // Was a valid number returned?
6282 GList *newlists = NULL;
6283 GList *newtps = NULL;
6288 /* accumulate trackpoint copies in newtps, in reverse order */
6289 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6291 if (count >= points) {
6292 /* flush accumulated trackpoints into new list */
6293 newlists = g_list_append(newlists, g_list_reverse(newtps));
6297 iter = g_list_next(iter);
6300 // If there is a remaining chunk put that into the new split list
6301 // This may well be the whole track if no split points were encountered
6303 newlists = g_list_append(newlists, g_list_reverse(newtps));
6306 /* put lists of trackpoints into tracks */
6308 // Only bother updating if the split results in new tracks
6309 if (g_list_length (newlists) > 1) {
6314 tr = vik_track_copy ( track, FALSE );
6315 tr->trackpoints = (GList *)(iter->data);
6317 if ( track->is_route ) {
6318 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6319 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6322 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6323 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6325 g_free ( new_tr_name );
6326 vik_track_calculate_bounds ( tr );
6328 iter = g_list_next(iter);
6330 // Remove original track and then update the display
6331 if ( track->is_route )
6332 vik_trw_layer_delete_route (vtl, track);
6334 vik_trw_layer_delete_track (vtl, track);
6335 vik_layer_emit_update(VIK_LAYER(vtl));
6337 g_list_free(newlists);
6341 * Split a track at the currently selected trackpoint
6343 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6345 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6346 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6347 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6351 * Split a track by its segments
6352 * Routes do not have segments so don't call this for routes
6354 static void trw_layer_split_segments ( menu_array_sublayer values )
6356 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6357 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6364 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6367 for ( i = 0; i < ntracks; i++ ) {
6369 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6370 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6371 g_free ( new_tr_name );
6376 // Remove original track
6377 vik_trw_layer_delete_track ( vtl, trk );
6378 vik_layer_emit_update ( VIK_LAYER(vtl) );
6381 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6384 /* end of split/merge routines */
6386 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6390 // Find available adjacent trackpoint
6391 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6392 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6393 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6395 // Delete current trackpoint
6396 vik_trackpoint_free ( vtl->current_tpl->data );
6397 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6399 // Set to current to the available adjacent trackpoint
6400 vtl->current_tpl = new_tpl;
6402 if ( vtl->current_tp_track ) {
6403 vik_track_calculate_bounds ( vtl->current_tp_track );
6407 // Delete current trackpoint
6408 vik_trackpoint_free ( vtl->current_tpl->data );
6409 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6410 trw_layer_cancel_current_tp ( vtl, FALSE );
6415 * Delete the selected point
6417 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6419 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6421 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6422 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6424 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6429 if ( !vtl->current_tpl )
6432 trw_layer_trackpoint_selected_delete ( vtl, trk );
6434 // Track has been updated so update tps:
6435 trw_layer_cancel_tps_of_track ( vtl, trk );
6437 vik_layer_emit_update ( VIK_LAYER(vtl) );
6441 * Delete adjacent track points at the same position
6442 * AKA Delete Dulplicates on the Properties Window
6444 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6446 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6448 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6449 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6451 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6456 gulong removed = vik_track_remove_dup_points ( trk );
6458 // Track has been updated so update tps:
6459 trw_layer_cancel_tps_of_track ( vtl, trk );
6461 // Inform user how much was deleted as it's not obvious from the normal view
6463 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6464 g_snprintf(str, 64, tmp_str, removed);
6465 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6467 vik_layer_emit_update ( VIK_LAYER(vtl) );
6471 * Delete adjacent track points with the same timestamp
6472 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6474 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6476 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6478 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6479 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6481 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6486 gulong removed = vik_track_remove_same_time_points ( trk );
6488 // Track has been updated so update tps:
6489 trw_layer_cancel_tps_of_track ( vtl, trk );
6491 // Inform user how much was deleted as it's not obvious from the normal view
6493 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6494 g_snprintf(str, 64, tmp_str, removed);
6495 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6497 vik_layer_emit_update ( VIK_LAYER(vtl) );
6503 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6505 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6507 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6508 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6510 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6515 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6517 vik_layer_emit_update ( VIK_LAYER(vtl) );
6520 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6522 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6524 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6525 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6527 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6532 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6534 vik_layer_emit_update ( VIK_LAYER(vtl) );
6540 static void trw_layer_reverse ( menu_array_sublayer values )
6542 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6544 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6545 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6547 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6552 vik_track_reverse ( track );
6554 vik_layer_emit_update ( VIK_LAYER(vtl) );
6558 * Open a diary at the specified date
6560 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6563 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6564 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6565 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6566 g_error_free ( err );
6572 * Open a diary at the date of the track or waypoint
6574 static void trw_layer_diary ( menu_array_sublayer values )
6576 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6578 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6579 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6585 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6586 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6587 trw_layer_diary_open ( vtl, date_buf );
6590 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6592 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6593 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6599 if ( wpt->has_timestamp ) {
6600 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6601 trw_layer_diary_open ( vtl, date_buf );
6604 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6609 * Similar to trw_layer_enum_item, but this uses a sorted method
6612 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6614 GList **list = (GList**)udata;
6615 // *list = g_list_prepend(*all, key); //unsorted method
6616 // Sort named list alphabetically
6617 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6622 * Now Waypoint specific sort
6624 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6626 GList **list = (GList**)udata;
6627 // Sort named list alphabetically
6628 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6632 * Track specific sort
6634 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6636 GList **list = (GList**)udata;
6637 // Sort named list alphabetically
6638 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6643 gboolean has_same_track_name;
6644 const gchar *same_track_name;
6645 } same_track_name_udata;
6647 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6649 const gchar* namea = (const gchar*) aa;
6650 const gchar* nameb = (const gchar*) bb;
6653 gint result = strcmp ( namea, nameb );
6655 if ( result == 0 ) {
6656 // Found two names the same
6657 same_track_name_udata *user_data = udata;
6658 user_data->has_same_track_name = TRUE;
6659 user_data->same_track_name = namea;
6662 // Leave ordering the same
6667 * Find out if any tracks have the same name in this hash table
6669 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6671 // Sort items by name, then compare if any next to each other are the same
6673 GList *track_names = NULL;
6674 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6677 if ( ! track_names )
6680 same_track_name_udata udata;
6681 udata.has_same_track_name = FALSE;
6683 // Use sort routine to traverse list comparing items
6684 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6685 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6686 // Still no tracks...
6690 return udata.has_same_track_name;
6694 * Force unqiue track names for the track table specified
6695 * Note the panel is a required parameter to enable the update of the names displayed
6696 * Specify if on tracks or else on routes
6698 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6700 // . Search list for an instance of repeated name
6701 // . get track of this name
6702 // . create new name
6703 // . rename track & update equiv. treeview iter
6704 // . repeat until all different
6706 same_track_name_udata udata;
6708 GList *track_names = NULL;
6709 udata.has_same_track_name = FALSE;
6710 udata.same_track_name = NULL;
6712 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6715 if ( ! track_names )
6718 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6720 // Still no tracks...
6721 if ( ! dummy_list1 )
6724 while ( udata.has_same_track_name ) {
6726 // Find a track with the same name
6729 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6731 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6735 g_critical("Houston, we've had a problem.");
6736 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6737 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6742 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6743 vik_track_set_name ( trk, newname );
6749 // Need want key of it for treeview update
6750 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6752 if ( trkf && udataU.uuid ) {
6756 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6758 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6761 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6763 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6765 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6769 // Start trying to find same names again...
6771 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6772 udata.has_same_track_name = FALSE;
6773 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6775 // No tracks any more - give up searching
6776 if ( ! dummy_list2 )
6777 udata.has_same_track_name = FALSE;
6781 vik_layers_panel_emit_update ( vlp );
6784 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6786 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6789 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6790 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6791 iter = &(vtl->tracks_iter);
6792 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6794 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6795 iter = &(vtl->routes_iter);
6796 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6798 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6799 iter = &(vtl->waypoints_iter);
6800 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6804 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6807 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6809 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6812 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6813 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6814 iter = &(vtl->tracks_iter);
6815 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6817 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6818 iter = &(vtl->routes_iter);
6819 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6821 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6822 iter = &(vtl->waypoints_iter);
6823 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6827 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6833 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6835 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6838 // Ensure list of track names offered is unique
6839 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6840 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6841 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6842 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6848 // Sort list alphabetically for better presentation
6849 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6852 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6856 // Get list of items to delete from the user
6857 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6860 _("Delete Selection"),
6861 _("Select tracks to delete"));
6864 // Delete requested tracks
6865 // since specificly requested, IMHO no need for extra confirmation
6866 if ( delete_list ) {
6868 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6869 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6870 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6872 g_list_free(delete_list);
6873 vik_layer_emit_update( VIK_LAYER(vtl) );
6880 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6882 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6885 // Ensure list of track names offered is unique
6886 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6887 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6888 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6889 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6895 // Sort list alphabetically for better presentation
6896 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6899 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6903 // Get list of items to delete from the user
6904 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6907 _("Delete Selection"),
6908 _("Select routes to delete") );
6911 // Delete requested routes
6912 // since specificly requested, IMHO no need for extra confirmation
6913 if ( delete_list ) {
6915 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6916 // This deletes first route it finds of that name (but uniqueness is enforced above)
6917 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6919 g_list_free(delete_list);
6920 vik_layer_emit_update( VIK_LAYER(vtl) );
6925 gboolean has_same_waypoint_name;
6926 const gchar *same_waypoint_name;
6927 } same_waypoint_name_udata;
6929 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6931 const gchar* namea = (const gchar*) aa;
6932 const gchar* nameb = (const gchar*) bb;
6935 gint result = strcmp ( namea, nameb );
6937 if ( result == 0 ) {
6938 // Found two names the same
6939 same_waypoint_name_udata *user_data = udata;
6940 user_data->has_same_waypoint_name = TRUE;
6941 user_data->same_waypoint_name = namea;
6944 // Leave ordering the same
6949 * Find out if any waypoints have the same name in this layer
6951 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6953 // Sort items by name, then compare if any next to each other are the same
6955 GList *waypoint_names = NULL;
6956 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6959 if ( ! waypoint_names )
6962 same_waypoint_name_udata udata;
6963 udata.has_same_waypoint_name = FALSE;
6965 // Use sort routine to traverse list comparing items
6966 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6967 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6968 // Still no waypoints...
6972 return udata.has_same_waypoint_name;
6976 * Force unqiue waypoint names for this layer
6977 * Note the panel is a required parameter to enable the update of the names displayed
6979 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6981 // . Search list for an instance of repeated name
6982 // . get waypoint of this name
6983 // . create new name
6984 // . rename waypoint & update equiv. treeview iter
6985 // . repeat until all different
6987 same_waypoint_name_udata udata;
6989 GList *waypoint_names = NULL;
6990 udata.has_same_waypoint_name = FALSE;
6991 udata.same_waypoint_name = NULL;
6993 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6996 if ( ! waypoint_names )
6999 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7001 // Still no waypoints...
7002 if ( ! dummy_list1 )
7005 while ( udata.has_same_waypoint_name ) {
7007 // Find a waypoint with the same name
7008 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7012 g_critical("Houston, we've had a problem.");
7013 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7014 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7019 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7021 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7023 // Start trying to find same names again...
7024 waypoint_names = NULL;
7025 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7026 udata.has_same_waypoint_name = FALSE;
7027 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7029 // No waypoints any more - give up searching
7030 if ( ! dummy_list2 )
7031 udata.has_same_waypoint_name = FALSE;
7035 vik_layers_panel_emit_update ( vlp );
7041 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7043 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7046 // Ensure list of waypoint names offered is unique
7047 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7048 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7049 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7050 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7056 // Sort list alphabetically for better presentation
7057 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7059 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7063 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7065 // Get list of items to delete from the user
7066 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7069 _("Delete Selection"),
7070 _("Select waypoints to delete"));
7073 // Delete requested waypoints
7074 // since specificly requested, IMHO no need for extra confirmation
7075 if ( delete_list ) {
7077 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7078 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7079 trw_layer_delete_waypoint_by_name (vtl, l->data);
7081 g_list_free(delete_list);
7083 trw_layer_calculate_bounds_waypoints ( vtl );
7084 vik_layer_emit_update( VIK_LAYER(vtl) );
7092 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7094 vik_treeview_item_toggle_visible ( vt, it );
7100 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7102 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7108 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7110 wp->visible = GPOINTER_TO_INT (on_off);
7116 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7118 wp->visible = !wp->visible;
7124 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7126 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7127 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7128 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7129 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7131 vik_layer_emit_update ( VIK_LAYER(vtl) );
7137 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7139 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7140 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7141 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7142 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7144 vik_layer_emit_update ( VIK_LAYER(vtl) );
7150 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7152 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7153 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7154 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7156 vik_layer_emit_update ( VIK_LAYER(vtl) );
7162 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7164 trk->visible = GPOINTER_TO_INT (on_off);
7170 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7172 trk->visible = !trk->visible;
7178 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7180 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7181 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7182 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7183 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7185 vik_layer_emit_update ( VIK_LAYER(vtl) );
7191 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7193 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7194 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7195 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7196 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7198 vik_layer_emit_update ( VIK_LAYER(vtl) );
7204 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7206 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7207 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7208 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7210 vik_layer_emit_update ( VIK_LAYER(vtl) );
7216 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7218 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7219 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7220 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7221 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7223 vik_layer_emit_update ( VIK_LAYER(vtl) );
7229 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7231 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7232 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7233 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7234 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7236 vik_layer_emit_update ( VIK_LAYER(vtl) );
7242 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7244 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7245 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7246 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7248 vik_layer_emit_update ( VIK_LAYER(vtl) );
7252 * vik_trw_layer_build_waypoint_list_t:
7254 * Helper function to construct a list of #vik_trw_waypoint_list_t
7256 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7258 GList *waypoints_and_layers = NULL;
7259 // build waypoints_and_layers list
7260 while ( waypoints ) {
7261 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7262 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7264 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7265 waypoints = g_list_next ( waypoints );
7267 return waypoints_and_layers;
7271 * trw_layer_create_waypoint_list:
7273 * Create the latest list of waypoints with the associated layer(s)
7274 * Although this will always be from a single layer here
7276 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7278 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7279 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7281 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7285 * trw_layer_analyse_close:
7287 * Stuff to do on dialog closure
7289 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7291 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7292 gtk_widget_destroy ( dialog );
7293 vtl->tracks_analysis_dialog = NULL;
7297 * vik_trw_layer_build_track_list_t:
7299 * Helper function to construct a list of #vik_trw_track_list_t
7301 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7303 GList *tracks_and_layers = NULL;
7304 // build tracks_and_layers list
7306 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7307 vtdl->trk = VIK_TRACK(tracks->data);
7309 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7310 tracks = g_list_next ( tracks );
7312 return tracks_and_layers;
7316 * trw_layer_create_track_list:
7318 * Create the latest list of tracks with the associated layer(s)
7319 * Although this will always be from a single layer here
7321 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7323 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7324 GList *tracks = NULL;
7325 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7326 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7328 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7330 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7333 static void trw_layer_tracks_stats ( menu_array_layer values )
7335 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7336 // There can only be one!
7337 if ( vtl->tracks_analysis_dialog )
7340 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7341 VIK_LAYER(vtl)->name,
7343 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7344 trw_layer_create_track_list,
7345 trw_layer_analyse_close );
7351 static void trw_layer_routes_stats ( menu_array_layer values )
7353 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7354 // There can only be one!
7355 if ( vtl->tracks_analysis_dialog )
7358 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7359 VIK_LAYER(vtl)->name,
7361 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7362 trw_layer_create_track_list,
7363 trw_layer_analyse_close );
7366 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7368 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7369 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7371 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7374 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7376 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7377 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7380 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7381 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7385 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7387 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7388 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7392 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7393 } else if ( !strncmp(wp->comment, "http", 4) ) {
7394 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7395 } else if ( !strncmp(wp->description, "http", 4) ) {
7396 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7400 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7402 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7404 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7406 // No actual change to the name supplied
7408 if (strcmp(newname, wp->name) == 0 )
7411 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7414 // An existing waypoint has been found with the requested name
7415 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7416 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7421 // Update WP name and refresh the treeview
7422 vik_waypoint_set_name (wp, newname);
7424 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7425 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7427 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7432 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7434 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7436 // No actual change to the name supplied
7438 if (strcmp(newname, trk->name) == 0)
7441 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7444 // An existing track has been found with the requested name
7445 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7446 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7450 // Update track name and refresh GUI parts
7451 vik_track_set_name (trk, newname);
7453 // Update any subwindows that could be displaying this track which has changed name
7454 // Only one Track Edit Window
7455 if ( l->current_tp_track == trk && l->tpwin ) {
7456 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7458 // Property Dialog of the track
7459 vik_trw_layer_propwin_update ( trk );
7461 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7462 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7464 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7469 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7471 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7473 // No actual change to the name supplied
7475 if (strcmp(newname, trk->name) == 0)
7478 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7481 // An existing track has been found with the requested name
7482 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7483 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7487 // Update track name and refresh GUI parts
7488 vik_track_set_name (trk, newname);
7490 // Update any subwindows that could be displaying this track which has changed name
7491 // Only one Track Edit Window
7492 if ( l->current_tp_track == trk && l->tpwin ) {
7493 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7495 // Property Dialog of the track
7496 vik_trw_layer_propwin_update ( trk );
7498 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7499 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7501 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7508 static gboolean is_valid_geocache_name ( gchar *str )
7510 gint len = strlen ( str );
7511 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]));
7514 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7516 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7517 a_acquire_set_filter_track ( trk );
7520 #ifdef VIK_CONFIG_GOOGLE
7521 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7523 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7524 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7527 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7529 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7531 gchar *escaped = uri_escape ( tr->comment );
7532 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7533 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7540 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7541 /* viewpoint is now available instead */
7542 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7544 static menu_array_sublayer pass_along;
7546 gboolean rv = FALSE;
7548 pass_along[MA_VTL] = l;
7549 pass_along[MA_VLP] = vlp;
7550 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7551 pass_along[MA_SUBLAYER_ID] = sublayer;
7552 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7553 pass_along[MA_VVP] = vvp;
7554 pass_along[MA_TV_ITER] = iter;
7555 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7557 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7561 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7562 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7563 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7564 gtk_widget_show ( item );
7566 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7567 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7568 if (tr && tr->property_dialog)
7569 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7571 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7572 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7573 if (tr && tr->property_dialog)
7574 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7577 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7578 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7579 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7580 gtk_widget_show ( item );
7582 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7585 gtk_widget_show ( item );
7587 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7588 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7589 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7590 gtk_widget_show ( item );
7592 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7594 // Always create separator as now there is always at least the transform menu option
7595 item = gtk_menu_item_new ();
7596 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7597 gtk_widget_show ( item );
7599 /* could be a right-click using the tool */
7600 if ( vlp != NULL ) {
7601 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7604 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7605 gtk_widget_show ( item );
7608 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7610 if ( wp && wp->name ) {
7611 if ( is_valid_geocache_name ( wp->name ) ) {
7612 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7614 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7615 gtk_widget_show ( item );
7617 #ifdef VIK_CONFIG_GEOTAG
7618 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7620 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7621 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7622 gtk_widget_show ( item );
7626 if ( wp && wp->image )
7628 // Set up image paramater
7629 pass_along[MA_MISC] = wp->image;
7631 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7632 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
7633 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7634 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7635 gtk_widget_show ( item );
7637 #ifdef VIK_CONFIG_GEOTAG
7638 GtkWidget *geotag_submenu = gtk_menu_new ();
7639 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7640 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7641 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7642 gtk_widget_show ( item );
7643 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7645 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7646 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7647 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7648 gtk_widget_show ( item );
7650 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7651 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7652 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7653 gtk_widget_show ( item );
7660 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7661 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7662 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7663 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7664 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7665 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7666 gtk_widget_show ( item );
7672 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7673 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7674 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7675 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7676 gtk_widget_show ( item );
7677 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7678 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7679 gtk_widget_set_sensitive ( item, TRUE );
7681 gtk_widget_set_sensitive ( item, FALSE );
7684 item = gtk_menu_item_new ();
7685 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7686 gtk_widget_show ( item );
7689 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7692 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7693 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7694 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7695 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7696 gtk_widget_show ( item );
7699 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7701 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7702 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7703 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7704 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7705 gtk_widget_show ( item );
7707 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7708 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7710 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7711 gtk_widget_show ( item );
7713 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7717 gtk_widget_show ( item );
7719 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7722 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7723 gtk_widget_show ( item );
7725 GtkWidget *vis_submenu = gtk_menu_new ();
7726 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7727 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7728 gtk_widget_show ( item );
7729 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7731 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7732 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7734 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7735 gtk_widget_show ( item );
7737 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7738 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7739 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7740 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7741 gtk_widget_show ( item );
7743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7746 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7747 gtk_widget_show ( item );
7749 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7752 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7755 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7759 if ( l->current_track && !l->current_track->is_route ) {
7760 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7761 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7762 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7763 gtk_widget_show ( item );
7765 item = gtk_menu_item_new ();
7766 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7767 gtk_widget_show ( item );
7770 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7771 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7773 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7774 gtk_widget_show ( item );
7776 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7777 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7779 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7780 gtk_widget_show ( item );
7781 // Make it available only when a new track *not* already in progress
7782 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7784 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7785 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7787 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7788 gtk_widget_show ( item );
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7793 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7794 gtk_widget_show ( item );
7796 GtkWidget *vis_submenu = gtk_menu_new ();
7797 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7798 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7799 gtk_widget_show ( item );
7800 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7802 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7803 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7805 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7806 gtk_widget_show ( item );
7808 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7812 gtk_widget_show ( item );
7814 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7817 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7819 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7820 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7821 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7822 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7823 gtk_widget_show ( item );
7825 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7827 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7828 gtk_widget_show ( item );
7831 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7835 if ( l->current_track && l->current_track->is_route ) {
7836 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7837 // Reuse finish track method
7838 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7839 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7840 gtk_widget_show ( item );
7842 item = gtk_menu_item_new ();
7843 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7844 gtk_widget_show ( item );
7847 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7848 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7849 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7850 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7851 gtk_widget_show ( item );
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7854 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7855 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7856 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7857 gtk_widget_show ( item );
7858 // Make it available only when a new track *not* already in progress
7859 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7861 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7862 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7863 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7864 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7865 gtk_widget_show ( item );
7867 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7868 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7870 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7871 gtk_widget_show ( item );
7873 GtkWidget *vis_submenu = gtk_menu_new ();
7874 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7875 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7876 gtk_widget_show ( item );
7877 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7879 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7880 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7882 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7883 gtk_widget_show ( item );
7885 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7888 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7889 gtk_widget_show ( item );
7891 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7892 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7894 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7896 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7897 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7899 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7901 gtk_widget_show ( item );
7903 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7905 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7906 gtk_widget_show ( item );
7910 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7911 GtkWidget *submenu_sort = gtk_menu_new ();
7912 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7913 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7914 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7915 gtk_widget_show ( item );
7916 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7918 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7919 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7921 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7922 gtk_widget_show ( item );
7924 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7925 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7927 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7928 gtk_widget_show ( item );
7931 GtkWidget *upload_submenu = gtk_menu_new ();
7933 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7935 item = gtk_menu_item_new ();
7936 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7937 gtk_widget_show ( item );
7939 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7940 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7941 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7942 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7943 if ( l->current_track ) {
7944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7945 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7946 gtk_widget_show ( item );
7949 item = gtk_menu_item_new ();
7950 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7951 gtk_widget_show ( item );
7954 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7955 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7957 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7958 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7960 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7961 gtk_widget_show ( item );
7963 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7964 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7965 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7966 gtk_widget_show ( item );
7968 GtkWidget *goto_submenu;
7969 goto_submenu = gtk_menu_new ();
7970 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7972 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7973 gtk_widget_show ( item );
7974 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7979 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7980 gtk_widget_show ( item );
7982 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7983 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7985 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7986 gtk_widget_show ( item );
7988 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7989 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7991 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7992 gtk_widget_show ( item );
7994 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7995 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7996 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7997 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7998 gtk_widget_show ( item );
8000 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8003 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8004 gtk_widget_show ( item );
8006 // Routes don't have speeds
8007 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8008 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8009 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8011 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8012 gtk_widget_show ( item );
8015 GtkWidget *combine_submenu;
8016 combine_submenu = gtk_menu_new ();
8017 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8018 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8019 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8020 gtk_widget_show ( item );
8021 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8023 // Routes don't have times or segments...
8024 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8025 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8027 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8028 gtk_widget_show ( item );
8030 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8032 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8033 gtk_widget_show ( item );
8036 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), 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 Track...") );
8044 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8046 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8047 gtk_widget_show ( item );
8049 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8050 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8052 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8054 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8055 gtk_widget_show ( item );
8057 GtkWidget *split_submenu;
8058 split_submenu = gtk_menu_new ();
8059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8061 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8062 gtk_widget_show ( item );
8063 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8065 // Routes don't have times or segments...
8066 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8067 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8069 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8070 gtk_widget_show ( item );
8072 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8073 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8075 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8076 gtk_widget_show ( item );
8079 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8081 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8082 gtk_widget_show ( item );
8084 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8085 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8086 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8087 gtk_widget_show ( item );
8088 // Make it available only when a trackpoint is selected.
8089 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8091 GtkWidget *insert_submenu = gtk_menu_new ();
8092 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8093 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8094 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8095 gtk_widget_show ( item );
8096 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8098 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8100 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8101 gtk_widget_show ( item );
8102 // Make it available only when a point is selected
8103 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8105 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8107 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8108 gtk_widget_show ( item );
8109 // Make it available only when a point is selected
8110 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8112 GtkWidget *delete_submenu;
8113 delete_submenu = gtk_menu_new ();
8114 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8115 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8116 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8117 gtk_widget_show ( item );
8118 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8120 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8121 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8123 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8124 gtk_widget_show ( item );
8125 // Make it available only when a point is selected
8126 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8128 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8129 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8130 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8131 gtk_widget_show ( item );
8133 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8135 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8136 gtk_widget_show ( item );
8138 GtkWidget *transform_submenu;
8139 transform_submenu = gtk_menu_new ();
8140 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8142 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8143 gtk_widget_show ( item );
8144 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8146 GtkWidget *dem_submenu;
8147 dem_submenu = gtk_menu_new ();
8148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8149 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
8150 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8151 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8153 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8155 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8156 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8157 gtk_widget_show ( item );
8159 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8161 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8162 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8163 gtk_widget_show ( item );
8165 GtkWidget *smooth_submenu;
8166 smooth_submenu = gtk_menu_new ();
8167 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8168 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8169 gtk_widget_show ( item );
8170 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8172 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8174 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8175 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8176 gtk_widget_show ( item );
8178 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8180 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8181 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8182 gtk_widget_show ( item );
8184 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8185 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8187 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8188 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8190 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8191 gtk_widget_show ( item );
8193 // Routes don't have timestamps - so this is only available for tracks
8194 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8195 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8197 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8198 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8199 gtk_widget_show ( item );
8202 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8203 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8205 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8208 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8209 gtk_widget_show ( item );
8211 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8212 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8215 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8216 gtk_widget_show ( item );
8219 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8221 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8222 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8224 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8225 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
8226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8227 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8228 gtk_widget_show ( item );
8231 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8232 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8234 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8235 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8236 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8237 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8238 gtk_widget_show ( item );
8240 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8241 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8243 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8244 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8246 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8247 gtk_widget_show ( item );
8249 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8250 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8251 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
8252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8253 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8254 gtk_widget_show ( item );
8257 // ATM can't upload a single waypoint but can do waypoints to a GPS
8258 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8259 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8260 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8261 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8262 gtk_widget_show ( item );
8263 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8265 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8266 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8267 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8268 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8269 gtk_widget_show ( item );
8273 // Only made available if a suitable program is installed
8274 if ( have_diary_program ) {
8275 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8276 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8279 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8280 gtk_widget_show ( item );
8284 #ifdef VIK_CONFIG_GOOGLE
8285 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8287 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8288 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8289 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8290 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8291 gtk_widget_show ( item );
8295 // Some things aren't usable with routes
8296 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8297 #ifdef VIK_CONFIG_OPENSTREETMAP
8298 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8299 // Convert internal pointer into track
8300 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8301 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8302 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8303 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8304 gtk_widget_show ( item );
8307 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8308 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8309 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8310 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8311 gtk_widget_show ( item );
8313 /* ATM This function is only available via the layers panel, due to needing a vlp */
8315 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8316 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8317 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8319 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8320 gtk_widget_show ( item );
8324 #ifdef VIK_CONFIG_GEOTAG
8325 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8326 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8327 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8328 gtk_widget_show ( item );
8332 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8333 // Only show on viewport popmenu when a trackpoint is selected
8334 if ( ! vlp && l->current_tpl ) {
8336 item = gtk_menu_item_new ();
8337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8338 gtk_widget_show ( item );
8340 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8343 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8344 gtk_widget_show ( item );
8348 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8349 GtkWidget *transform_submenu;
8350 transform_submenu = gtk_menu_new ();
8351 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8352 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8353 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8354 gtk_widget_show ( item );
8355 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8357 GtkWidget *dem_submenu;
8358 dem_submenu = gtk_menu_new ();
8359 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8360 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
8361 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8362 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8364 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8365 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8366 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8367 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8368 gtk_widget_show ( item );
8370 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8372 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8373 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8374 gtk_widget_show ( item );
8377 gtk_widget_show_all ( GTK_WIDGET(menu) );
8382 // TODO: Probably better to rework this track manipulation in viktrack.c
8383 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8386 if (!vtl->current_tpl)
8389 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8390 VikTrackpoint *tp_other = NULL;
8393 if (!vtl->current_tpl->prev)
8395 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8397 if (!vtl->current_tpl->next)
8399 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8402 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8405 VikTrackpoint *tp_new = vik_trackpoint_new();
8406 struct LatLon ll_current, ll_other;
8407 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8408 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8410 /* main positional interpolation */
8411 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8412 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8414 /* Now other properties that can be interpolated */
8415 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8417 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8418 /* Note here the division is applied to each part, then added
8419 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8420 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8421 tp_new->has_timestamp = TRUE;
8424 if (tp_current->speed != NAN && tp_other->speed != NAN)
8425 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8427 /* TODO - improve interpolation of course, as it may not be correct.
8428 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8429 [similar applies if value is in radians] */
8430 if (tp_current->course != NAN && tp_other->course != NAN)
8431 tp_new->course = (tp_current->course + tp_other->course)/2;
8433 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8435 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8436 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8438 // Otherwise try routes
8439 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8443 gint index = g_list_index ( trk->trackpoints, tp_current );
8447 // NB no recalculation of bounds since it is inserted between points
8448 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8453 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8459 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8463 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8465 if ( vtl->current_tpl )
8467 vtl->current_tpl = NULL;
8468 vtl->current_tp_track = NULL;
8469 vtl->current_tp_id = NULL;
8470 vik_layer_emit_update(VIK_LAYER(vtl));
8474 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8476 g_assert ( vtl->tpwin != NULL );
8477 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8478 trw_layer_cancel_current_tp ( vtl, TRUE );
8480 if ( vtl->current_tpl == NULL )
8483 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8485 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8486 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8488 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8490 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8492 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8496 trw_layer_trackpoint_selected_delete ( vtl, tr );
8498 if ( vtl->current_tpl )
8499 // Reset dialog with the available adjacent trackpoint
8500 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8502 vik_layer_emit_update(VIK_LAYER(vtl));
8504 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8506 if ( vtl->current_tp_track )
8507 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8508 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8510 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8512 if ( vtl->current_tp_track )
8513 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8514 vik_layer_emit_update(VIK_LAYER(vtl));
8516 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8518 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8519 vik_layer_emit_update(VIK_LAYER(vtl));
8521 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8522 vik_layer_emit_update(VIK_LAYER(vtl));
8526 * trw_layer_dialog_shift:
8527 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8529 * Try to reposition a dialog if it's over the specified coord
8530 * so to not obscure the item of interest
8532 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8534 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8536 // Attempt force dialog to be shown so we can find out where it is more reliably...
8537 while ( gtk_events_pending() )
8538 gtk_main_iteration ();
8540 // get parent window position & size
8541 gint win_pos_x, win_pos_y;
8542 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8544 gint win_size_x, win_size_y;
8545 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8547 // get own dialog size
8548 gint dia_size_x, dia_size_y;
8549 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8551 // get own dialog position
8552 gint dia_pos_x, dia_pos_y;
8553 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8555 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8556 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8558 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8560 gint vp_xx, vp_yy; // In viewport pixels
8561 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8563 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8567 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8569 // Transform Viewport pixels into absolute pixels
8570 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8571 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8573 // Is dialog over the point (to within an ^^ edge value)
8574 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8575 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8579 gint hh = vik_viewport_get_height ( vvp );
8581 // Consider the difference in viewport to the full window
8582 gint offset_y = dest_y;
8583 // Add difference between dialog and window sizes
8584 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8586 if ( vp_yy > hh/2 ) {
8587 // Point in bottom half, move window to top half
8588 gtk_window_move ( dialog, dia_pos_x, offset_y );
8591 // Point in top half, move dialog down
8592 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8596 // Shift left<->right
8597 gint ww = vik_viewport_get_width ( vvp );
8599 // Consider the difference in viewport to the full window
8600 gint offset_x = dest_x;
8601 // Add difference between dialog and window sizes
8602 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8604 if ( vp_xx > ww/2 ) {
8605 // Point on right, move window to left
8606 gtk_window_move ( dialog, offset_x, dia_pos_y );
8609 // Point on left, move right
8610 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8618 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8622 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8623 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8624 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8625 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8627 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8629 if ( vtl->current_tpl ) {
8630 // get tp pixel position
8631 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8633 // Shift up<->down to try not to obscure the trackpoint.
8634 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8638 if ( vtl->current_tpl )
8639 if ( vtl->current_tp_track )
8640 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8641 /* set layer name and TP data */
8644 /***************************************************************************
8646 ***************************************************************************/
8648 /*** Utility data structures and functions ****/
8652 gint closest_x, closest_y;
8653 gboolean draw_images;
8654 gpointer *closest_wp_id;
8655 VikWaypoint *closest_wp;
8661 gint closest_x, closest_y;
8662 gpointer closest_track_id;
8663 VikTrackpoint *closest_tp;
8669 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8675 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8677 // If waypoint has an image then use the image size to select
8678 if ( params->draw_images && wp->image ) {
8679 gint slackx, slacky;
8680 slackx = wp->image_width / 2;
8681 slacky = wp->image_height / 2;
8683 if ( x <= params->x + slackx && x >= params->x - slackx
8684 && y <= params->y + slacky && y >= params->y - slacky ) {
8685 params->closest_wp_id = id;
8686 params->closest_wp = wp;
8687 params->closest_x = x;
8688 params->closest_y = y;
8691 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8692 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8693 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8695 params->closest_wp_id = id;
8696 params->closest_wp = wp;
8697 params->closest_x = x;
8698 params->closest_y = y;
8702 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8704 GList *tpl = t->trackpoints;
8710 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8716 tp = VIK_TRACKPOINT(tpl->data);
8718 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8720 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8721 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8722 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8724 params->closest_track_id = id;
8725 params->closest_tp = tp;
8726 params->closest_tpl = tpl;
8727 params->closest_x = x;
8728 params->closest_y = y;
8734 // ATM: Leave this as 'Track' only.
8735 // Not overly bothered about having a snap to route trackpoint capability
8736 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8738 TPSearchParams params;
8742 params.closest_track_id = NULL;
8743 params.closest_tp = NULL;
8744 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8745 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8746 return params.closest_tp;
8749 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8751 WPSearchParams params;
8755 params.draw_images = vtl->drawimages;
8756 params.closest_wp = NULL;
8757 params.closest_wp_id = NULL;
8758 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8759 return params.closest_wp;
8763 // Some forward declarations
8764 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8765 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8766 static void marker_end_move ( tool_ed_t *t );
8769 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8773 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8775 // Here always allow snapping back to the original location
8776 // this is useful when one decides not to move the thing afterall
8777 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8780 if ( event->state & GDK_CONTROL_MASK )
8782 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8784 new_coord = tp->coord;
8788 if ( event->state & GDK_SHIFT_MASK )
8790 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8792 new_coord = wp->coord;
8796 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8798 marker_moveto ( t, x, y );
8805 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8807 if ( t->holding && event->button == 1 )
8810 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8813 if ( event->state & GDK_CONTROL_MASK )
8815 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8817 new_coord = tp->coord;
8821 if ( event->state & GDK_SHIFT_MASK )
8823 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8825 new_coord = wp->coord;
8828 marker_end_move ( t );
8830 // Determine if working on a waypoint or a trackpoint
8831 if ( t->is_waypoint ) {
8832 // Update waypoint position
8833 vtl->current_wp->coord = new_coord;
8834 trw_layer_calculate_bounds_waypoints ( vtl );
8835 // Reset waypoint pointer
8836 vtl->current_wp = NULL;
8837 vtl->current_wp_id = NULL;
8840 if ( vtl->current_tpl ) {
8841 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8843 if ( vtl->current_tp_track )
8844 vik_track_calculate_bounds ( vtl->current_tp_track );
8847 if ( vtl->current_tp_track )
8848 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8849 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8853 vik_layer_emit_update ( VIK_LAYER(vtl) );
8860 Returns true if a waypoint or track is found near the requested event position for this particular layer
8861 The item found is automatically selected
8862 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8864 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8866 if ( event->button != 1 )
8869 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8872 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8876 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8878 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8880 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8881 WPSearchParams wp_params;
8882 wp_params.vvp = vvp;
8883 wp_params.x = event->x;
8884 wp_params.y = event->y;
8885 wp_params.draw_images = vtl->drawimages;
8886 wp_params.closest_wp_id = NULL;
8887 wp_params.closest_wp = NULL;
8889 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8891 if ( wp_params.closest_wp ) {
8894 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8896 // Too easy to move it so must be holding shift to start immediately moving it
8897 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8898 if ( event->state & GDK_SHIFT_MASK ||
8899 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8900 // Put into 'move buffer'
8901 // NB vvp & vw already set in tet
8902 tet->vtl = (gpointer)vtl;
8903 tet->is_waypoint = TRUE;
8905 marker_begin_move (tet, event->x, event->y);
8908 vtl->current_wp = wp_params.closest_wp;
8909 vtl->current_wp_id = wp_params.closest_wp_id;
8911 if ( event->type == GDK_2BUTTON_PRESS ) {
8912 if ( vtl->current_wp->image ) {
8913 menu_array_sublayer values;
8914 values[MA_VTL] = vtl;
8915 values[MA_MISC] = vtl->current_wp->image;
8916 trw_layer_show_picture ( values );
8920 vik_layer_emit_update ( VIK_LAYER(vtl) );
8926 // Used for both track and route lists
8927 TPSearchParams tp_params;
8928 tp_params.vvp = vvp;
8929 tp_params.x = event->x;
8930 tp_params.y = event->y;
8931 tp_params.closest_track_id = NULL;
8932 tp_params.closest_tp = NULL;
8933 tp_params.closest_tpl = NULL;
8934 tp_params.bbox = bbox;
8936 if (vtl->tracks_visible) {
8937 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8939 if ( tp_params.closest_tp ) {
8941 // Always select + highlight the track
8942 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8944 tet->is_waypoint = FALSE;
8946 // Select the Trackpoint
8947 // Can move it immediately when control held or it's the previously selected tp
8948 if ( event->state & GDK_CONTROL_MASK ||
8949 vtl->current_tpl == tp_params.closest_tpl ) {
8950 // Put into 'move buffer'
8951 // NB vvp & vw already set in tet
8952 tet->vtl = (gpointer)vtl;
8953 marker_begin_move (tet, event->x, event->y);
8956 vtl->current_tpl = tp_params.closest_tpl;
8957 vtl->current_tp_id = tp_params.closest_track_id;
8958 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8960 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8963 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8965 vik_layer_emit_update ( VIK_LAYER(vtl) );
8970 // Try again for routes
8971 if (vtl->routes_visible) {
8972 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8974 if ( tp_params.closest_tp ) {
8976 // Always select + highlight the track
8977 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8979 tet->is_waypoint = FALSE;
8981 // Select the Trackpoint
8982 // Can move it immediately when control held or it's the previously selected tp
8983 if ( event->state & GDK_CONTROL_MASK ||
8984 vtl->current_tpl == tp_params.closest_tpl ) {
8985 // Put into 'move buffer'
8986 // NB vvp & vw already set in tet
8987 tet->vtl = (gpointer)vtl;
8988 marker_begin_move (tet, event->x, event->y);
8991 vtl->current_tpl = tp_params.closest_tpl;
8992 vtl->current_tp_id = tp_params.closest_track_id;
8993 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8995 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8998 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9000 vik_layer_emit_update ( VIK_LAYER(vtl) );
9005 /* these aren't the droids you're looking for */
9006 vtl->current_wp = NULL;
9007 vtl->current_wp_id = NULL;
9008 trw_layer_cancel_current_tp ( vtl, FALSE );
9011 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9016 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9018 if ( event->button != 3 )
9021 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9024 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9027 /* Post menu for the currently selected item */
9029 /* See if a track is selected */
9030 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9031 if ( track && track->visible ) {
9033 if ( track->name ) {
9035 if ( vtl->track_right_click_menu )
9036 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9038 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9045 if ( track->is_route )
9046 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9048 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9050 if ( trkf && udataU.uuid ) {
9053 if ( track->is_route )
9054 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9056 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9058 trw_layer_sublayer_add_menu_items ( vtl,
9059 vtl->track_right_click_menu,
9061 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9067 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9073 /* See if a waypoint is selected */
9074 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9075 if ( waypoint && waypoint->visible ) {
9076 if ( waypoint->name ) {
9078 if ( vtl->wp_right_click_menu )
9079 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9081 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9084 udata.wp = waypoint;
9087 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9089 if ( wpf && udata.uuid ) {
9090 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9092 trw_layer_sublayer_add_menu_items ( vtl,
9093 vtl->wp_right_click_menu,
9095 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9100 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9109 /* background drawing hook, to be passed the viewport */
9110 static gboolean tool_sync_done = TRUE;
9112 static gboolean tool_sync(gpointer data)
9114 VikViewport *vvp = data;
9115 gdk_threads_enter();
9116 vik_viewport_sync(vvp);
9117 tool_sync_done = TRUE;
9118 gdk_threads_leave();
9122 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9125 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9126 gdk_gc_set_function ( t->gc, GDK_INVERT );
9127 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9128 vik_viewport_sync(t->vvp);
9133 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9135 VikViewport *vvp = t->vvp;
9136 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9137 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9141 if (tool_sync_done) {
9142 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9143 tool_sync_done = FALSE;
9147 static void marker_end_move ( tool_ed_t *t )
9149 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9150 g_object_unref ( t->gc );
9154 /*** Edit waypoint ****/
9156 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9158 tool_ed_t *t = g_new(tool_ed_t, 1);
9164 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9169 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9171 WPSearchParams params;
9172 tool_ed_t *t = data;
9173 VikViewport *vvp = t->vvp;
9175 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9182 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9185 if ( vtl->current_wp && vtl->current_wp->visible )
9187 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9189 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9191 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9192 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9194 if ( event->button == 3 )
9195 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9197 marker_begin_move(t, event->x, event->y);
9204 params.x = event->x;
9205 params.y = event->y;
9206 params.draw_images = vtl->drawimages;
9207 params.closest_wp_id = NULL;
9208 params.closest_wp = NULL;
9209 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9210 if ( vtl->current_wp && (vtl->current_wp == 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 marker_begin_move(t, event->x, event->y);
9218 else if ( params.closest_wp )
9220 if ( event->button == 3 )
9221 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9223 vtl->waypoint_rightclick = FALSE;
9225 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9227 vtl->current_wp = params.closest_wp;
9228 vtl->current_wp_id = params.closest_wp_id;
9230 /* could make it so don't update if old WP is off screen and new is null but oh well */
9231 vik_layer_emit_update ( VIK_LAYER(vtl) );
9235 vtl->current_wp = NULL;
9236 vtl->current_wp_id = NULL;
9237 vtl->waypoint_rightclick = FALSE;
9238 vik_layer_emit_update ( VIK_LAYER(vtl) );
9242 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9244 tool_ed_t *t = data;
9245 VikViewport *vvp = t->vvp;
9247 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9252 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9255 if ( event->state & GDK_CONTROL_MASK )
9257 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9259 new_coord = tp->coord;
9263 if ( event->state & GDK_SHIFT_MASK )
9265 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9266 if ( wp && wp != vtl->current_wp )
9267 new_coord = wp->coord;
9272 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9274 marker_moveto ( t, x, y );
9281 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9283 tool_ed_t *t = data;
9284 VikViewport *vvp = t->vvp;
9286 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9289 if ( t->holding && event->button == 1 )
9292 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9295 if ( event->state & GDK_CONTROL_MASK )
9297 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9299 new_coord = tp->coord;
9303 if ( event->state & GDK_SHIFT_MASK )
9305 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9306 if ( wp && wp != vtl->current_wp )
9307 new_coord = wp->coord;
9310 marker_end_move ( t );
9312 vtl->current_wp->coord = new_coord;
9314 trw_layer_calculate_bounds_waypoints ( vtl );
9315 vik_layer_emit_update ( VIK_LAYER(vtl) );
9318 /* PUT IN RIGHT PLACE!!! */
9319 if ( event->button == 3 && vtl->waypoint_rightclick )
9321 if ( vtl->wp_right_click_menu )
9322 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9323 if ( vtl->current_wp ) {
9324 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9325 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 );
9326 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9328 vtl->waypoint_rightclick = FALSE;
9333 /*** New track ****/
9335 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9342 GdkDrawable *drawable;
9348 * Draw specified pixmap
9350 static gboolean draw_sync ( gpointer data )
9352 draw_sync_t *ds = (draw_sync_t*) data;
9353 // Sometimes don't want to draw
9354 // normally because another update has taken precedent such as panning the display
9355 // which means this pixmap is no longer valid
9356 if ( ds->vtl->draw_sync_do ) {
9357 gdk_threads_enter();
9358 gdk_draw_drawable (ds->drawable,
9361 0, 0, 0, 0, -1, -1);
9362 ds->vtl->draw_sync_done = TRUE;
9363 gdk_threads_leave();
9369 static gchar* distance_string (gdouble distance)
9373 /* draw label with distance */
9374 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9375 switch (dist_units) {
9376 case VIK_UNITS_DISTANCE_MILES:
9377 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9378 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9379 } else if (distance < 1609.4) {
9380 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9382 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9386 // VIK_UNITS_DISTANCE_KILOMETRES
9387 if (distance >= 1000 && distance < 100000) {
9388 g_sprintf(str, "%3.2f km", distance/1000.0);
9389 } else if (distance < 1000) {
9390 g_sprintf(str, "%d m", (int)distance);
9392 g_sprintf(str, "%d km", (int)distance/1000);
9396 return g_strdup (str);
9400 * Actually set the message in statusbar
9402 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9404 // Only show elevation data when track has some elevation properties
9405 gchar str_gain_loss[64];
9406 str_gain_loss[0] = '\0';
9407 gchar str_last_step[64];
9408 str_last_step[0] = '\0';
9409 gchar *str_total = distance_string (distance);
9411 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9412 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9413 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9415 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9418 if ( last_step > 0 ) {
9419 gchar *tmp = distance_string (last_step);
9420 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9424 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9426 // Write with full gain/loss information
9427 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9428 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9430 g_free ( str_total );
9434 * Figure out what information should be set in the statusbar and then write it
9436 static void update_statusbar ( VikTrwLayer *vtl )
9438 // Get elevation data
9439 gdouble elev_gain, elev_loss;
9440 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9442 /* Find out actual distance of current track */
9443 gdouble distance = vik_track_get_length (vtl->current_track);
9445 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9449 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9451 /* if we haven't sync'ed yet, we don't have time to do more. */
9452 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9453 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9455 static GdkPixmap *pixmap = NULL;
9457 // Need to check in case window has been resized
9458 w1 = vik_viewport_get_width(vvp);
9459 h1 = vik_viewport_get_height(vvp);
9461 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9463 gdk_drawable_get_size (pixmap, &w2, &h2);
9464 if (w1 != w2 || h1 != h2) {
9465 g_object_unref ( G_OBJECT ( pixmap ) );
9466 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9469 // Reset to background
9470 gdk_draw_drawable (pixmap,
9471 vtl->current_track_newpoint_gc,
9472 vik_viewport_get_pixmap(vvp),
9473 0, 0, 0, 0, -1, -1);
9475 draw_sync_t *passalong;
9478 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9480 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9481 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9482 // thus when we come to reset to the background it would include what we have already drawn!!
9483 gdk_draw_line ( pixmap,
9484 vtl->current_track_newpoint_gc,
9485 x1, y1, event->x, event->y );
9486 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9488 /* Find out actual distance of current track */
9489 gdouble distance = vik_track_get_length (vtl->current_track);
9491 // Now add distance to where the pointer is //
9494 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9495 vik_coord_to_latlon ( &coord, &ll );
9496 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9497 distance = distance + last_step;
9499 // Get elevation data
9500 gdouble elev_gain, elev_loss;
9501 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9503 // Adjust elevation data (if available) for the current pointer position
9505 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9506 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9507 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9508 // Adjust elevation of last track point
9509 if ( elev_new > last_tpt->altitude )
9511 elev_gain += elev_new - last_tpt->altitude;
9514 elev_loss += last_tpt->altitude - elev_new;
9519 // Display of the distance 'tooltip' during track creation is controlled by a preference
9521 if ( a_vik_get_create_track_tooltip() ) {
9523 gchar *str = distance_string (distance);
9525 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9526 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9527 pango_layout_set_text (pl, str, -1);
9529 pango_layout_get_pixel_size ( pl, &wd, &hd );
9532 // offset from cursor a bit depending on font size
9536 // Create a background block to make the text easier to read over the background map
9537 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9538 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9539 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9541 g_object_unref ( G_OBJECT ( pl ) );
9542 g_object_unref ( G_OBJECT ( background_block_gc ) );
9546 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9547 passalong->vtl = vtl;
9548 passalong->pixmap = pixmap;
9549 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9550 passalong->gc = vtl->current_track_newpoint_gc;
9554 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9556 // Update statusbar with full gain/loss information
9557 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9559 // draw pixmap when we have time to
9560 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9561 vtl->draw_sync_done = FALSE;
9562 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9564 return VIK_LAYER_TOOL_ACK;
9567 // NB vtl->current_track must be valid
9568 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9571 if ( vtl->current_track->trackpoints ) {
9572 // TODO rework this...
9573 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9574 GList *last = g_list_last(vtl->current_track->trackpoints);
9575 g_free ( last->data );
9576 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9578 vik_track_calculate_bounds ( vtl->current_track );
9582 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9584 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9585 vtl->current_track = NULL;
9586 vik_layer_emit_update ( VIK_LAYER(vtl) );
9588 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9589 undo_trackpoint_add ( vtl );
9590 update_statusbar ( vtl );
9591 vik_layer_emit_update ( VIK_LAYER(vtl) );
9598 * Common function to handle trackpoint button requests on either a route or a track
9599 * . enables adding a point via normal click
9600 * . enables removal of last point via right click
9601 * . finishing of the track or route via double clicking
9603 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9607 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9610 if ( event->button == 2 ) {
9611 // As the display is panning, the new track pixmap is now invalid so don't draw it
9612 // otherwise this drawing done results in flickering back to an old image
9613 vtl->draw_sync_do = FALSE;
9617 if ( event->button == 3 )
9619 if ( !vtl->current_track )
9621 undo_trackpoint_add ( vtl );
9622 update_statusbar ( vtl );
9623 vik_layer_emit_update ( VIK_LAYER(vtl) );
9627 if ( event->type == GDK_2BUTTON_PRESS )
9629 /* subtract last (duplicate from double click) tp then end */
9630 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9632 /* undo last, then end */
9633 undo_trackpoint_add ( vtl );
9634 vtl->current_track = NULL;
9636 vik_layer_emit_update ( VIK_LAYER(vtl) );
9640 tp = vik_trackpoint_new();
9641 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9643 /* snap to other TP */
9644 if ( event->state & GDK_CONTROL_MASK )
9646 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9648 tp->coord = other_tp->coord;
9651 tp->newsegment = FALSE;
9652 tp->has_timestamp = FALSE;
9655 if ( vtl->current_track ) {
9656 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9657 /* Auto attempt to get elevation from DEM data (if it's available) */
9658 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9661 vtl->ct_x1 = vtl->ct_x2;
9662 vtl->ct_y1 = vtl->ct_y2;
9663 vtl->ct_x2 = event->x;
9664 vtl->ct_y2 = event->y;
9666 vik_layer_emit_update ( VIK_LAYER(vtl) );
9670 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9672 // if we were running the route finder, cancel it
9673 vtl->route_finder_started = FALSE;
9675 // ----------------------------------------------------- if current is a route - switch to new track
9676 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9678 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9679 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9681 new_track_create_common ( vtl, name );
9687 return tool_new_track_or_route_click ( vtl, event, vvp );
9690 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9692 if ( event->button == 2 ) {
9693 // Pan moving ended - enable potential point drawing again
9694 vtl->draw_sync_do = TRUE;
9695 vtl->draw_sync_done = TRUE;
9699 /*** New route ****/
9701 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9706 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9708 // if we were running the route finder, cancel it
9709 vtl->route_finder_started = FALSE;
9711 // -------------------------- if current is a track - switch to new route,
9712 if ( event->button == 1 && ( ! vtl->current_track ||
9713 (vtl->current_track && !vtl->current_track->is_route ) ) )
9715 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9716 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9717 new_route_create_common ( vtl, name );
9723 return tool_new_track_or_route_click ( vtl, event, vvp );
9726 /*** New waypoint ****/
9728 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9733 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9736 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9738 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9739 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9740 trw_layer_calculate_bounds_waypoints ( vtl );
9741 vik_layer_emit_update ( VIK_LAYER(vtl) );
9747 /*** Edit trackpoint ****/
9749 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9751 tool_ed_t *t = g_new(tool_ed_t, 1);
9757 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9762 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9764 tool_ed_t *t = data;
9765 VikViewport *vvp = t->vvp;
9766 TPSearchParams params;
9767 /* OUTDATED DOCUMENTATION:
9768 find 5 pixel range on each side. then put these UTM, and a pointer
9769 to the winning track name (and maybe the winning track itself), and a
9770 pointer to the winning trackpoint, inside an array or struct. pass
9771 this along, do a foreach on the tracks which will do a foreach on the
9774 params.x = event->x;
9775 params.y = event->y;
9776 params.closest_track_id = NULL;
9777 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9778 params.closest_tp = NULL;
9779 params.closest_tpl = NULL;
9780 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9782 if ( event->button != 1 )
9785 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9788 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9791 if ( vtl->current_tpl )
9793 /* 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.) */
9794 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9795 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9800 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9802 if ( current_tr->visible &&
9803 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9804 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9805 marker_begin_move ( t, event->x, event->y );
9811 if ( vtl->tracks_visible )
9812 g_hash_table_foreach ( vtl->tracks, (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->tracks_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->tracks, 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 if ( vtl->routes_visible )
9827 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9829 if ( params.closest_tp )
9831 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9832 vtl->current_tpl = params.closest_tpl;
9833 vtl->current_tp_id = params.closest_track_id;
9834 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9835 trw_layer_tpwin_init ( vtl );
9836 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9837 vik_layer_emit_update ( VIK_LAYER(vtl) );
9841 /* these aren't the droids you're looking for */
9845 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9847 tool_ed_t *t = data;
9848 VikViewport *vvp = t->vvp;
9850 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9856 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9859 if ( event->state & GDK_CONTROL_MASK )
9861 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9862 if ( tp && tp != vtl->current_tpl->data )
9863 new_coord = tp->coord;
9865 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9868 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9869 marker_moveto ( t, x, y );
9877 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9879 tool_ed_t *t = data;
9880 VikViewport *vvp = t->vvp;
9882 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9884 if ( event->button != 1)
9889 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9892 if ( event->state & GDK_CONTROL_MASK )
9894 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9895 if ( tp && tp != vtl->current_tpl->data )
9896 new_coord = tp->coord;
9899 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9900 if ( vtl->current_tp_track )
9901 vik_track_calculate_bounds ( vtl->current_tp_track );
9903 marker_end_move ( t );
9905 /* diff dist is diff from orig */
9907 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9909 vik_layer_emit_update ( VIK_LAYER(vtl) );
9916 /*** Extended Route Finder ***/
9918 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9923 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9926 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9929 vik_layer_emit_update ( VIK_LAYER(vtl) );
9931 /* remove last ' to:...' */
9932 if ( vtl->current_track->comment ) {
9933 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9934 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9935 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9936 last_to - vtl->current_track->comment - 1);
9937 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9944 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9947 if ( !vtl ) return FALSE;
9948 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9949 if ( event->button == 3 && vtl->current_track ) {
9950 tool_extended_route_finder_undo ( vtl );
9952 else if ( event->button == 2 ) {
9953 vtl->draw_sync_do = FALSE;
9956 // if we started the track but via undo deleted all the track points, begin again
9957 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
9958 return tool_new_track_or_route_click ( vtl, event, vvp );
9960 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
9961 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
9962 struct LatLon start, end;
9964 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
9965 vik_coord_to_latlon ( &(tp_start->coord), &start );
9966 vik_coord_to_latlon ( &(tmp), &end );
9968 vtl->route_finder_started = TRUE;
9969 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9971 // update UI to let user know what's going on
9972 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9973 VikRoutingEngine *engine = vik_routing_default_engine ( );
9975 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
9978 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
9979 vik_routing_engine_get_label ( engine ),
9980 start.lat, start.lon, end.lat, end.lon );
9981 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
9983 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
9986 /* Give GTK a change to display the new status bar before querying the web */
9987 while ( gtk_events_pending ( ) )
9988 gtk_main_iteration ( );
9990 gboolean find_status = vik_routing_default_find ( vtl, start, end );
9992 /* Update UI to say we're done */
9993 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
9994 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
9995 vik_routing_engine_get_label ( engine ),
9996 start.lat, start.lon, end.lat, end.lon )
9997 : g_strdup_printf ( _("Error getting route from %s."),
9998 vik_routing_engine_get_label ( engine ) );
9999 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10002 vik_layer_emit_update ( VIK_LAYER(vtl) );
10004 vtl->current_track = NULL;
10006 // create a new route where we will add the planned route to
10007 gboolean ret = tool_new_route_click( vtl, event, vvp );
10009 vtl->route_finder_started = TRUE;
10016 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10018 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10019 vtl->route_finder_started = FALSE;
10020 vtl->current_track = NULL;
10021 vik_layer_emit_update ( VIK_LAYER(vtl) );
10023 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10024 tool_extended_route_finder_undo ( vtl );
10031 /*** Show picture ****/
10033 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10038 /* Params are: vvp, event, last match found or NULL */
10039 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10041 if ( wp->image && wp->visible )
10043 gint x, y, slackx, slacky;
10044 GdkEventButton *event = (GdkEventButton *) params[1];
10046 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10047 slackx = wp->image_width / 2;
10048 slacky = wp->image_height / 2;
10049 if ( x <= event->x + slackx && x >= event->x - slackx
10050 && y <= event->y + slacky && y >= event->y - slacky )
10052 params[2] = wp->image; /* we've found a match. however continue searching
10053 * since we want to find the last match -- that
10054 * is, the match that was drawn last. */
10059 static void trw_layer_show_picture ( menu_array_sublayer values )
10061 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10063 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10064 #else /* WINDOWS */
10065 GError *err = NULL;
10066 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10067 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10068 g_free ( quoted_file );
10069 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10071 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() );
10072 g_error_free ( err );
10075 #endif /* WINDOWS */
10078 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10080 gpointer params[3] = { vvp, event, NULL };
10081 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10083 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10086 static menu_array_sublayer values;
10087 values[MA_VTL] = vtl;
10088 values[MA_MISC] = params[2];
10089 trw_layer_show_picture ( values );
10090 return TRUE; /* found a match */
10093 return FALSE; /* go through other layers, searching for a match */
10096 /***************************************************************************
10098 ***************************************************************************/
10101 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10103 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10104 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10107 /* Structure for thumbnail creating data used in the background thread */
10109 VikTrwLayer *vtl; // Layer needed for redrawing
10110 GSList *pics; // Image list
10111 } thumbnail_create_thread_data;
10113 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10115 guint total = g_slist_length(tctd->pics), done = 0;
10116 while ( tctd->pics )
10118 a_thumbnails_create ( (gchar *) tctd->pics->data );
10119 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10121 return -1; /* Abort thread */
10123 tctd->pics = tctd->pics->next;
10126 // Redraw to show the thumbnails as they are now created
10127 if ( IS_VIK_LAYER(tctd->vtl) )
10128 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10133 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10135 while ( tctd->pics )
10137 g_free ( tctd->pics->data );
10138 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10143 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10145 if ( ! vtl->has_verified_thumbnails )
10147 GSList *pics = NULL;
10148 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10151 gint len = g_slist_length ( pics );
10152 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10153 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10156 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10158 (vik_thr_func) create_thumbnails_thread,
10160 (vik_thr_free_func) thumbnail_create_thread_free,
10168 static const gchar* my_track_colors ( gint ii )
10170 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10182 // Fast and reliable way of returning a colour
10183 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10186 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10188 GHashTableIter iter;
10189 gpointer key, value;
10193 g_hash_table_iter_init ( &iter, vtl->tracks );
10195 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10197 // Tracks get a random spread of colours if not already assigned
10198 if ( ! VIK_TRACK(value)->has_color ) {
10199 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10200 VIK_TRACK(value)->color = vtl->track_color;
10202 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10204 VIK_TRACK(value)->has_color = TRUE;
10207 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10210 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10216 g_hash_table_iter_init ( &iter, vtl->routes );
10218 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10220 // Routes get an intermix of reds
10221 if ( ! VIK_TRACK(value)->has_color ) {
10223 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10225 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10226 VIK_TRACK(value)->has_color = TRUE;
10229 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10236 * (Re)Calculate the bounds of the waypoints in this layer,
10237 * This should be called whenever waypoints are changed
10239 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10241 struct LatLon topleft = { 0.0, 0.0 };
10242 struct LatLon bottomright = { 0.0, 0.0 };
10245 GHashTableIter iter;
10246 gpointer key, value;
10248 g_hash_table_iter_init ( &iter, vtl->waypoints );
10250 // Set bounds to first point
10251 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10252 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10253 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10256 // Ensure there is another point...
10257 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10259 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10261 // See if this point increases the bounds.
10262 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10264 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10265 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10266 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10267 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10271 vtl->waypoints_bbox.north = topleft.lat;
10272 vtl->waypoints_bbox.east = bottomright.lon;
10273 vtl->waypoints_bbox.south = bottomright.lat;
10274 vtl->waypoints_bbox.west = topleft.lon;
10277 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10279 vik_track_calculate_bounds ( trk );
10282 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10284 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10285 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10288 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10290 if ( ! VIK_LAYER(vtl)->vt )
10293 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10294 if ( g_hash_table_size (vtl->tracks) > 1 )
10295 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10297 if ( g_hash_table_size (vtl->routes) > 1 )
10298 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10300 if ( g_hash_table_size (vtl->waypoints) > 1 )
10301 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10304 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10306 if ( VIK_LAYER(vtl)->realized )
10307 trw_layer_verify_thumbnails ( vtl, vvp );
10308 trw_layer_track_alloc_colors ( vtl );
10310 trw_layer_calculate_bounds_waypoints ( vtl );
10311 trw_layer_calculate_bounds_tracks ( vtl );
10313 // Apply treeview sort after loading all the tracks for this layer
10314 // (rather than sorted insert on each individual track additional)
10315 // and after subsequent changes to the properties as the specified order may have changed.
10316 // since the sorting of a treeview section is now very quick
10317 // NB sorting is also performed after every name change as well to maintain the list order
10318 trw_layer_sort_all ( vtl );
10320 // Setting metadata time if not otherwise set
10321 if ( vtl->metadata ) {
10323 gboolean need_to_set_time = TRUE;
10324 if ( vtl->metadata->timestamp ) {
10325 need_to_set_time = FALSE;
10326 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10327 need_to_set_time = TRUE;
10330 if ( need_to_set_time ) {
10331 // Could rewrite this as a general get first time of a TRW Layer function
10332 GTimeVal timestamp;
10333 timestamp.tv_usec = 0;
10334 gboolean has_timestamp = FALSE;
10337 gl = g_hash_table_get_values ( vtl->tracks );
10338 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10339 gl = g_list_first ( gl );
10341 // Check times of tracks
10343 // Only need to check the first track as they have been sorted by time
10344 VikTrack *trk = (VikTrack*)gl->data;
10345 // Assume trackpoints already sorted by time
10346 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10347 if ( tpt && tpt->has_timestamp ) {
10348 timestamp.tv_sec = tpt->timestamp;
10349 has_timestamp = TRUE;
10351 g_list_free ( gl );
10354 if ( !has_timestamp ) {
10355 // 'Last' resort - current time
10356 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10357 g_get_current_time ( ×tamp );
10359 // Check times of waypoints
10360 gl = g_hash_table_get_values ( vtl->waypoints );
10362 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10363 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10364 if ( wpt->has_timestamp ) {
10365 if ( timestamp.tv_sec > wpt->timestamp ) {
10366 timestamp.tv_sec = wpt->timestamp;
10367 has_timestamp = TRUE;
10371 g_list_free ( gl );
10374 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10379 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10381 return vtl->coord_mode;
10385 * Uniquify the whole layer
10386 * Also requires the layers panel as the names shown there need updating too
10387 * Returns whether the operation was successful or not
10389 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10391 if ( vtl && vlp ) {
10392 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10393 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10394 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10400 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10402 vik_coord_convert ( &(wp->coord), *dest_mode );
10405 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10407 vik_track_convert ( tr, *dest_mode );
10410 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10412 if ( vtl->coord_mode != dest_mode )
10414 vtl->coord_mode = dest_mode;
10415 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10416 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10417 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10421 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10423 vtl->menu_selection = selection;
10426 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10428 return (vtl->menu_selection);
10431 /* ----------- Downloading maps along tracks --------------- */
10433 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10435 /* TODO: calculating based on current size of viewport */
10436 const gdouble w_at_zoom_0_125 = 0.0013;
10437 const gdouble h_at_zoom_0_125 = 0.0011;
10438 gdouble zoom_factor = zoom_level/0.125;
10440 wh->lat = h_at_zoom_0_125 * zoom_factor;
10441 wh->lon = w_at_zoom_0_125 * zoom_factor;
10443 return 0; /* all OK */
10446 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10448 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10449 (dist->lat >= ABS(to->north_south - from->north_south)))
10452 VikCoord *coord = g_malloc(sizeof(VikCoord));
10453 coord->mode = VIK_COORD_LATLON;
10455 if (ABS(gradient) < 1) {
10456 if (from->east_west > to->east_west)
10457 coord->east_west = from->east_west - dist->lon;
10459 coord->east_west = from->east_west + dist->lon;
10460 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10462 if (from->north_south > to->north_south)
10463 coord->north_south = from->north_south - dist->lat;
10465 coord->north_south = from->north_south + dist->lat;
10466 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10472 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10474 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10475 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10477 VikCoord *next = from;
10479 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10481 list = g_list_prepend(list, next);
10487 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10489 typedef struct _Rect {
10494 #define GLRECT(iter) ((Rect *)((iter)->data))
10497 GList *rects_to_download = NULL;
10500 if (get_download_area_width(vvp, zoom_level, &wh))
10503 GList *iter = tr->trackpoints;
10507 gboolean new_map = TRUE;
10508 VikCoord *cur_coord, tl, br;
10511 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10513 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10514 rect = g_malloc(sizeof(Rect));
10517 rect->center = *cur_coord;
10518 rects_to_download = g_list_prepend(rects_to_download, rect);
10523 gboolean found = FALSE;
10524 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10525 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10536 GList *fillins = NULL;
10537 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10538 /* seems that ATM the function get_next_coord works only for LATLON */
10539 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10540 /* fill-ins for far apart points */
10541 GList *cur_rect, *next_rect;
10542 for (cur_rect = rects_to_download;
10543 (next_rect = cur_rect->next) != NULL;
10544 cur_rect = cur_rect->next) {
10545 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10546 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10547 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10551 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10554 GList *fiter = fillins;
10556 cur_coord = (VikCoord *)(fiter->data);
10557 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10558 rect = g_malloc(sizeof(Rect));
10561 rect->center = *cur_coord;
10562 rects_to_download = g_list_prepend(rects_to_download, rect);
10563 fiter = fiter->next;
10567 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10568 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10572 for (iter = fillins; iter; iter = iter->next)
10573 g_free(iter->data);
10574 g_list_free(fillins);
10576 if (rects_to_download) {
10577 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10578 g_free(rect_iter->data);
10579 g_list_free(rects_to_download);
10583 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10587 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10588 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10589 gint selected_zoom, default_zoom;
10591 VikTrwLayer *vtl = values[MA_VTL];
10592 VikLayersPanel *vlp = values[MA_VLP];
10594 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10595 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10597 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10601 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10603 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10604 int num_maps = g_list_length(vmls);
10607 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10611 // Convert from list of vmls to list of names. Allowing the user to select one of them
10612 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10613 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10615 gchar **np = map_names;
10616 VikMapsLayer **lp = map_layers;
10618 for (i = 0; i < num_maps; i++) {
10619 vml = (VikMapsLayer *)(vmls->data);
10621 *np++ = vik_maps_layer_get_map_label(vml);
10624 // Mark end of the array lists
10628 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10629 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10630 if (cur_zoom == zoom_vals[default_zoom])
10633 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10635 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10638 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10641 for (i = 0; i < num_maps; i++)
10642 g_free(map_names[i]);
10644 g_free(map_layers);
10650 /**** lowest waypoint number calculation ***/
10651 static gint highest_wp_number_name_to_number(const gchar *name) {
10652 if ( strlen(name) == 3 ) {
10653 int n = atoi(name);
10654 if ( n < 100 && name[0] != '0' )
10656 if ( n < 10 && name[0] != '0' )
10664 static void highest_wp_number_reset(VikTrwLayer *vtl)
10666 vtl->highest_wp_number = -1;
10669 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10671 /* if is bigger that top, add it */
10672 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10673 if ( new_wp_num > vtl->highest_wp_number )
10674 vtl->highest_wp_number = new_wp_num;
10677 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10679 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10680 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10681 if ( vtl->highest_wp_number == old_wp_num ) {
10683 vtl->highest_wp_number--;
10685 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10686 /* search down until we find something that *does* exist */
10688 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10689 vtl->highest_wp_number--;
10690 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10695 /* get lowest unused number */
10696 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10699 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10701 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10702 return g_strdup(buf);
10706 * trw_layer_create_track_list_both:
10708 * Create the latest list of tracks and routes
10710 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10712 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10713 GList *tracks = NULL;
10714 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10715 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10717 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10720 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10722 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10724 gchar *title = NULL;
10725 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10726 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10728 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10730 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10734 static void trw_layer_track_list_dialog ( menu_array_layer values )
10736 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10738 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10739 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10743 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10745 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10747 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10748 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );