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 && vik_track_get_tp_first(tr)->has_timestamp ) {
2760 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2761 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2762 if ( trkpt_last->has_timestamp ) {
2764 t1 = vik_track_get_tp_first(tr)->timestamp;
2765 t2 = trkpt_last->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);
2789 * Generate tooltip text for the layer.
2790 * This is relatively complicated as it considers information for
2791 * no tracks, a single track or multiple tracks
2792 * (which may or may not have timing information)
2794 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2805 static gchar tmp_buf[128];
2808 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2810 // Safety check - I think these should always be valid
2811 if ( vtl->tracks && vtl->waypoints ) {
2812 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2813 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2815 GDate* gdate_start = g_date_new ();
2816 g_date_set_time_t (gdate_start, tt.start_time);
2818 GDate* gdate_end = g_date_new ();
2819 g_date_set_time_t (gdate_end, tt.end_time);
2821 if ( g_date_compare (gdate_start, gdate_end) ) {
2822 // Dates differ so print range on separate line
2823 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2824 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2825 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2828 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2829 if ( tt.start_time != 0 )
2830 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2834 if ( tt.length > 0.0 ) {
2835 gdouble len_in_units;
2837 // Setup info dependent on distance units
2838 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2839 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2840 len_in_units = VIK_METERS_TO_MILES(tt.length);
2843 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2844 len_in_units = tt.length/1000.0;
2847 // Timing information if available
2849 if ( tt.duration > 0 ) {
2850 g_snprintf (tbuf1, sizeof(tbuf1),
2851 _(" in %d:%02d hrs:mins"),
2852 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2854 g_snprintf (tbuf2, sizeof(tbuf2),
2855 _("\n%sTotal Length %.1f %s%s"),
2856 tbuf3, len_in_units, tbuf4, tbuf1);
2859 // Put together all the elements to form compact tooltip text
2860 g_snprintf (tmp_buf, sizeof(tmp_buf),
2861 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2862 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2864 g_date_free (gdate_start);
2865 g_date_free (gdate_end);
2872 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2876 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2878 // Very simple tooltip - may expand detail in the future...
2879 static gchar tmp_buf[32];
2880 g_snprintf (tmp_buf, sizeof(tmp_buf),
2882 g_hash_table_size (l->tracks));
2886 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2888 // Very simple tooltip - may expand detail in the future...
2889 static gchar tmp_buf[32];
2890 g_snprintf (tmp_buf, sizeof(tmp_buf),
2892 g_hash_table_size (l->routes));
2897 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2898 // Same tooltip for a route
2899 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2902 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2903 tr = g_hash_table_lookup ( l->tracks, sublayer );
2905 tr = g_hash_table_lookup ( l->routes, sublayer );
2908 // Could be a better way of handling strings - but this works...
2909 gchar time_buf1[20];
2910 gchar time_buf2[20];
2911 time_buf1[0] = '\0';
2912 time_buf2[0] = '\0';
2913 static gchar tmp_buf[100];
2914 // Compact info: Short date eg (11/20/99), duration and length
2915 // Hopefully these are the things that are most useful and so promoted into the tooltip
2916 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2917 // %x The preferred date representation for the current locale without the time.
2918 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2919 time_t dur = vik_track_get_duration ( tr );
2921 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2923 // Get length and consider the appropriate distance units
2924 gdouble tr_len = vik_track_get_length(tr);
2925 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2926 switch (dist_units) {
2927 case VIK_UNITS_DISTANCE_KILOMETRES:
2928 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2930 case VIK_UNITS_DISTANCE_MILES:
2931 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2940 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2942 // Very simple tooltip - may expand detail in the future...
2943 static gchar tmp_buf[32];
2944 g_snprintf (tmp_buf, sizeof(tmp_buf),
2946 g_hash_table_size (l->waypoints));
2950 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2952 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2953 // NB It's OK to return NULL
2958 return w->description;
2967 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2970 * set_statusbar_msg_info_trkpt:
2972 * Function to show track point information on the statusbar
2973 * Items displayed is controlled by the settings format code
2975 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2977 gchar *statusbar_format_code = NULL;
2978 gboolean need2free = FALSE;
2979 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2980 // Otherwise use default
2981 statusbar_format_code = g_strdup ( "KEATDN" );
2985 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2986 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2990 g_free ( statusbar_format_code );
2994 * Function to show basic waypoint information on the statusbar
2996 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2999 switch (a_vik_get_units_height ()) {
3000 case VIK_UNITS_HEIGHT_FEET:
3001 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3004 //VIK_UNITS_HEIGHT_METRES:
3005 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3009 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3010 // one can easily use the current pointer position to see this if needed
3011 gchar *lat = NULL, *lon = NULL;
3012 static struct LatLon ll;
3013 vik_coord_to_latlon (&(wpt->coord), &ll);
3014 a_coords_latlon_to_string ( &ll, &lat, &lon );
3016 // Combine parts to make overall message
3019 // Add comment if available
3020 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3022 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3023 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3030 * General layer selection function, find out which bit is selected and take appropriate action
3032 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3035 l->current_wp = NULL;
3036 l->current_wp_id = NULL;
3037 trw_layer_cancel_current_tp ( l, FALSE );
3040 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3044 case VIK_TREEVIEW_TYPE_LAYER:
3046 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3047 /* Mark for redraw */
3052 case VIK_TREEVIEW_TYPE_SUBLAYER:
3056 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3058 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3059 /* Mark for redraw */
3063 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3065 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3066 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3067 /* Mark for redraw */
3071 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3073 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3074 /* Mark for redraw */
3078 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3080 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3081 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3082 /* Mark for redraw */
3086 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3088 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3089 /* Mark for redraw */
3093 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3095 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3097 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3098 // Show some waypoint info
3099 set_statusbar_msg_info_wpt ( l, wpt );
3100 /* Mark for redraw */
3107 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3116 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3121 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3126 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3131 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3133 return l->waypoints;
3136 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3138 return vtl->tracks_iters;
3141 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3143 return vtl->routes_iters;
3146 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3148 return vtl->waypoints;
3151 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3153 return ! ( g_hash_table_size ( vtl->tracks ) ||
3154 g_hash_table_size ( vtl->routes ) ||
3155 g_hash_table_size ( vtl->waypoints ) );
3158 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3160 return vtl->tracks_visible;
3163 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3165 return vtl->routes_visible;
3168 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3170 return vtl->waypoints_visible;
3174 * ATM use a case sensitive find
3175 * Finds the first one
3177 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3179 if ( wp && wp->name )
3180 if ( ! strcmp ( wp->name, name ) )
3186 * Get waypoint by name - not guaranteed to be unique
3187 * Finds the first one
3189 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3191 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3195 * ATM use a case sensitive find
3196 * Finds the first one
3198 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3200 if ( trk && trk->name )
3201 if ( ! strcmp ( trk->name, name ) )
3207 * Get track by name - not guaranteed to be unique
3208 * Finds the first one
3210 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3212 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3216 * Get route by name - not guaranteed to be unique
3217 * Finds the first one
3219 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3221 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3224 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3226 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3227 maxmin[0].lat = trk->bbox.north;
3228 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3229 maxmin[1].lat = trk->bbox.south;
3230 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3231 maxmin[0].lon = trk->bbox.east;
3232 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3233 maxmin[1].lon = trk->bbox.west;
3236 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3238 // Continually reuse maxmin to find the latest maximum and minimum values
3239 // First set to waypoints bounds
3240 maxmin[0].lat = vtl->waypoints_bbox.north;
3241 maxmin[1].lat = vtl->waypoints_bbox.south;
3242 maxmin[0].lon = vtl->waypoints_bbox.east;
3243 maxmin[1].lon = vtl->waypoints_bbox.west;
3244 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3245 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3248 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3250 /* 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... */
3251 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3252 trw_layer_find_maxmin (vtl, maxmin);
3253 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3257 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3258 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3263 static void trw_layer_centerize ( menu_array_layer values )
3265 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3267 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3268 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3270 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3273 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3275 /* First set the center [in case previously viewing from elsewhere] */
3276 /* Then loop through zoom levels until provided positions are in view */
3277 /* This method is not particularly fast - but should work well enough */
3278 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3280 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3281 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3283 /* Convert into definite 'smallest' and 'largest' positions */
3284 struct LatLon minmin;
3285 if ( maxmin[0].lat < maxmin[1].lat )
3286 minmin.lat = maxmin[0].lat;
3288 minmin.lat = maxmin[1].lat;
3290 struct LatLon maxmax;
3291 if ( maxmin[0].lon > maxmin[1].lon )
3292 maxmax.lon = maxmin[0].lon;
3294 maxmax.lon = maxmin[1].lon;
3296 /* Never zoom in too far - generally not that useful, as too close ! */
3297 /* Always recalculate the 'best' zoom level */
3299 vik_viewport_set_zoom ( vvp, zoom );
3301 gdouble min_lat, max_lat, min_lon, max_lon;
3302 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3303 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3304 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3305 /* NB I think the logic used in this test to determine if the bounds is within view
3306 fails if track goes across 180 degrees longitude.
3307 Hopefully that situation is not too common...
3308 Mind you viking doesn't really do edge locations to well anyway */
3309 if ( min_lat < minmin.lat &&
3310 max_lat > minmin.lat &&
3311 min_lon < maxmax.lon &&
3312 max_lon > maxmax.lon )
3313 /* Found within zoom level */
3318 vik_viewport_set_zoom ( vvp, zoom );
3322 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3324 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3325 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3326 trw_layer_find_maxmin (vtl, maxmin);
3327 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3330 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3335 static void trw_layer_auto_view ( menu_array_layer values )
3337 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3338 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3339 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3340 vik_layers_panel_emit_update ( vlp );
3343 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3346 static void trw_layer_export_gpspoint ( menu_array_layer values )
3348 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3350 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3352 g_free ( auto_save_name );
3355 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3357 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3359 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3361 g_free ( auto_save_name );
3364 static void trw_layer_export_gpx ( menu_array_layer values )
3366 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3368 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3370 g_free ( auto_save_name );
3373 static void trw_layer_export_kml ( menu_array_layer values )
3375 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3377 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3379 g_free ( auto_save_name );
3382 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3384 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3385 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3388 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3390 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3393 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3395 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3398 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3400 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3402 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3403 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3405 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3407 if ( !trk || !trk->name )
3410 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3412 gchar *label = NULL;
3413 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3414 label = _("Export Route as GPX");
3416 label = _("Export Track as GPX");
3417 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3419 g_free ( auto_save_name );
3422 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3424 wpu_udata *user_data = udata;
3425 if ( wp == user_data->wp ) {
3426 user_data->uuid = id;
3432 static void trw_layer_goto_wp ( menu_array_layer values )
3434 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3435 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3436 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3437 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3438 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3440 GTK_RESPONSE_REJECT,
3442 GTK_RESPONSE_ACCEPT,
3445 GtkWidget *label, *entry;
3446 label = gtk_label_new(_("Waypoint Name:"));
3447 entry = gtk_entry_new();
3449 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3450 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3451 gtk_widget_show_all ( label );
3452 gtk_widget_show_all ( entry );
3454 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3456 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3458 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3459 // Find *first* wp with the given name
3460 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3463 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3466 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3467 vik_layers_panel_emit_update ( vlp );
3469 // Find and select on the side panel
3474 // Hmmm, want key of it
3475 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3477 if ( wpf && udata.uuid ) {
3478 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3479 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3488 gtk_widget_destroy ( dia );
3491 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3493 gchar *default_name = highest_wp_number_get(vtl);
3494 VikWaypoint *wp = vik_waypoint_new();
3495 gchar *returned_name;
3497 wp->coord = *def_coord;
3499 // Attempt to auto set height if DEM data is available
3500 vik_waypoint_apply_dem_data ( wp, TRUE );
3502 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3504 if ( returned_name )
3507 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3508 g_free (default_name);
3509 g_free (returned_name);
3512 g_free (default_name);
3513 vik_waypoint_free(wp);
3517 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3519 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3520 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3521 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3522 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3523 VikViewport *vvp = vik_window_viewport(vw);
3525 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3526 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3527 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3528 trw_layer_calculate_bounds_waypoints ( vtl );
3529 vik_layers_panel_emit_update ( vlp );
3532 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3534 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3535 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3536 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3538 trw_layer_find_maxmin (vtl, maxmin);
3539 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3540 trw_layer_calculate_bounds_waypoints ( vtl );
3541 vik_layers_panel_emit_update ( vlp );
3544 #ifdef VIK_CONFIG_GEOTAG
3545 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3547 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3549 // Update directly - not changing the mtime
3550 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3553 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3555 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3558 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3562 * Use code in separate file for this feature as reasonably complex
3564 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3567 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3568 // Unset so can be reverified later if necessary
3569 vtl->has_verified_thumbnails = FALSE;
3571 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3577 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3579 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3580 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3582 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3588 static void trw_layer_geotagging ( menu_array_layer values )
3590 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3591 // Unset so can be reverified later if necessary
3592 vtl->has_verified_thumbnails = FALSE;
3594 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3601 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3603 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3605 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3606 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3607 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3608 VikViewport *vvp = vik_window_viewport(vw);
3610 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3614 * Acquire into this TRW Layer straight from GPS Device
3616 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3618 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3619 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3623 * Acquire into this TRW Layer from Directions
3625 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3627 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3631 * Acquire into this TRW Layer from an entered URL
3633 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3635 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3636 trw_layer_acquire ( values, &vik_datasource_url_interface );
3639 #ifdef VIK_CONFIG_OPENSTREETMAP
3641 * Acquire into this TRW Layer from OSM
3643 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3645 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3649 * Acquire into this TRW Layer from OSM for 'My' Traces
3651 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3653 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3657 #ifdef VIK_CONFIG_GEOCACHES
3659 * Acquire into this TRW Layer from Geocaching.com
3661 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3663 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3667 #ifdef VIK_CONFIG_GEOTAG
3669 * Acquire into this TRW Layer from images
3671 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3673 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3675 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3676 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3678 // Reverify thumbnails as they may have changed
3679 vtl->has_verified_thumbnails = FALSE;
3680 trw_layer_verify_thumbnails ( vtl, NULL );
3684 static void trw_layer_gps_upload ( menu_array_layer values )
3686 menu_array_sublayer data;
3688 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3690 data[MA_VTL] = values[MA_VTL];
3691 data[MA_VLP] = values[MA_VLP];
3693 trw_layer_gps_upload_any ( data );
3697 * If pass_along[3] is defined that this will upload just that track
3699 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3701 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3702 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3704 // May not actually get a track here as values[2&3] can be null
3705 VikTrack *track = NULL;
3706 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3707 gboolean xfer_all = FALSE;
3709 if ( values[MA_SUBTYPE] ) {
3711 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3712 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3715 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3716 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3719 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3722 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3726 else if ( !values[MA_CONFIRM] )
3727 xfer_all = TRUE; // i.e. whole layer
3729 if (track && !track->visible) {
3730 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3734 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3735 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3736 GTK_DIALOG_DESTROY_WITH_PARENT,
3738 GTK_RESPONSE_ACCEPT,
3740 GTK_RESPONSE_REJECT,
3743 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3744 GtkWidget *response_w = NULL;
3745 #if GTK_CHECK_VERSION (2, 20, 0)
3746 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3750 gtk_widget_grab_focus ( response_w );
3752 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3754 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3755 datasource_gps_clean_up ( dgs );
3756 gtk_widget_destroy ( dialog );
3760 // Get info from reused datasource dialog widgets
3761 gchar* protocol = datasource_gps_get_protocol ( dgs );
3762 gchar* port = datasource_gps_get_descriptor ( dgs );
3763 // NB don't free the above strings as they're references to values held elsewhere
3764 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3765 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3766 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3767 gboolean turn_off = datasource_gps_get_off ( dgs );
3769 gtk_widget_destroy ( dialog );
3771 // When called from the viewport - work the corresponding layerspanel:
3773 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3776 // Apply settings to transfer to the GPS device
3783 vik_layers_panel_get_viewport (vlp),
3792 * Acquire into this TRW Layer from any GPS Babel supported file
3794 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3796 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3797 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3798 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3799 VikViewport *vvp = vik_window_viewport(vw);
3801 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3804 static void trw_layer_new_wp ( menu_array_layer values )
3806 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3807 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3808 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3809 instead return true if you want to update. */
3810 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 ) {
3811 trw_layer_calculate_bounds_waypoints ( vtl );
3812 vik_layers_panel_emit_update ( vlp );
3816 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3818 vtl->current_track = vik_track_new();
3819 vik_track_set_defaults ( vtl->current_track );
3820 vtl->current_track->visible = TRUE;
3821 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3822 // Create track with the preferred colour from the layer properties
3823 vtl->current_track->color = vtl->track_color;
3825 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3826 vtl->current_track->has_color = TRUE;
3827 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3830 static void trw_layer_new_track ( menu_array_layer values )
3832 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3834 if ( ! vtl->current_track ) {
3835 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3836 new_track_create_common ( vtl, name );
3839 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3843 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3845 vtl->current_track = vik_track_new();
3846 vik_track_set_defaults ( vtl->current_track );
3847 vtl->current_track->visible = TRUE;
3848 vtl->current_track->is_route = TRUE;
3849 // By default make all routes red
3850 vtl->current_track->has_color = TRUE;
3851 gdk_color_parse ( "red", &vtl->current_track->color );
3852 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3855 static void trw_layer_new_route ( menu_array_layer values )
3857 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3859 if ( ! vtl->current_track ) {
3860 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3861 new_route_create_common ( vtl, name );
3863 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3867 static void trw_layer_auto_routes_view ( menu_array_layer values )
3869 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3870 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3872 if ( g_hash_table_size (vtl->routes) > 0 ) {
3873 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3874 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3875 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3876 vik_layers_panel_emit_update ( vlp );
3881 static void trw_layer_finish_track ( menu_array_layer values )
3883 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3884 vtl->current_track = NULL;
3885 vtl->route_finder_started = FALSE;
3886 vik_layer_emit_update ( VIK_LAYER(vtl) );
3889 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3891 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3892 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3894 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3895 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3896 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3897 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3898 vik_layers_panel_emit_update ( vlp );
3902 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3904 /* NB do not care if wp is visible or not */
3905 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3908 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3910 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3911 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3913 /* Only 1 waypoint - jump straight to it */
3914 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3915 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3916 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3918 /* If at least 2 waypoints - find center and then zoom to fit */
3919 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3921 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3922 maxmin[0].lat = vtl->waypoints_bbox.north;
3923 maxmin[1].lat = vtl->waypoints_bbox.south;
3924 maxmin[0].lon = vtl->waypoints_bbox.east;
3925 maxmin[1].lon = vtl->waypoints_bbox.west;
3926 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3929 vik_layers_panel_emit_update ( vlp );
3932 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3934 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3937 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3939 if ( values[MA_MISC] ) {
3940 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3941 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3945 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3947 static menu_array_layer pass_along;
3949 GtkWidget *export_submenu;
3950 pass_along[MA_VTL] = vtl;
3951 pass_along[MA_VLP] = vlp;
3953 item = gtk_menu_item_new();
3954 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3955 gtk_widget_show ( item );
3957 if ( vtl->current_track ) {
3958 if ( vtl->current_track->is_route )
3959 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3961 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3963 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3964 gtk_widget_show ( item );
3967 item = gtk_menu_item_new ();
3968 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3969 gtk_widget_show ( item );
3972 /* Now with icons */
3973 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3974 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3975 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3976 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3977 gtk_widget_show ( item );
3979 GtkWidget *view_submenu = gtk_menu_new();
3980 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3981 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3982 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3983 gtk_widget_show ( item );
3984 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3986 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3988 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3989 gtk_widget_show ( item );
3991 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3992 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3993 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3994 gtk_widget_show ( item );
3996 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3998 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3999 gtk_widget_show ( item );
4001 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4002 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4004 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4005 gtk_widget_show ( item );
4007 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4010 gtk_widget_show ( item );
4012 export_submenu = gtk_menu_new ();
4013 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4015 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4016 gtk_widget_show ( item );
4017 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4019 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4021 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4022 gtk_widget_show ( item );
4024 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4026 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4027 gtk_widget_show ( item );
4029 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4031 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4032 gtk_widget_show ( item );
4034 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4036 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4037 gtk_widget_show ( item );
4039 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4041 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4042 gtk_widget_show ( item );
4044 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4045 item = gtk_menu_item_new_with_mnemonic ( external1 );
4046 g_free ( external1 );
4047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4048 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4049 gtk_widget_show ( item );
4051 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4052 item = gtk_menu_item_new_with_mnemonic ( external2 );
4053 g_free ( external2 );
4054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4055 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4056 gtk_widget_show ( item );
4058 GtkWidget *new_submenu = gtk_menu_new();
4059 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4061 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4062 gtk_widget_show(item);
4063 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4065 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4068 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4069 gtk_widget_show ( item );
4071 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4074 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4075 gtk_widget_show ( item );
4076 // Make it available only when a new track *not* already in progress
4077 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4079 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4080 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4082 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4083 gtk_widget_show ( item );
4084 // Make it available only when a new track *not* already in progress
4085 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4087 #ifdef VIK_CONFIG_GEOTAG
4088 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4090 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4091 gtk_widget_show ( item );
4094 GtkWidget *acquire_submenu = gtk_menu_new ();
4095 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4096 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4097 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4098 gtk_widget_show ( item );
4099 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4101 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4102 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4103 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4104 gtk_widget_show ( item );
4106 /* FIXME: only add menu when at least a routing engine has support for Directions */
4107 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4109 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4110 gtk_widget_show ( item );
4112 #ifdef VIK_CONFIG_OPENSTREETMAP
4113 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4115 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4116 gtk_widget_show ( item );
4118 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4120 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4121 gtk_widget_show ( item );
4124 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4126 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4127 gtk_widget_show ( item );
4129 #ifdef VIK_CONFIG_GEONAMES
4130 GtkWidget *wikipedia_submenu = gtk_menu_new();
4131 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4133 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4134 gtk_widget_show(item);
4135 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4137 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4138 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4139 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4140 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4141 gtk_widget_show ( item );
4143 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4144 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4145 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4146 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4147 gtk_widget_show ( item );
4150 #ifdef VIK_CONFIG_GEOCACHES
4151 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4153 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4154 gtk_widget_show ( item );
4157 #ifdef VIK_CONFIG_GEOTAG
4158 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4160 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4161 gtk_widget_show ( item );
4164 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4166 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4167 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4168 gtk_widget_show ( item );
4170 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4172 GtkWidget *upload_submenu = gtk_menu_new ();
4173 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4174 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4175 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4176 gtk_widget_show ( item );
4177 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4179 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4180 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4181 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4182 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4183 gtk_widget_show ( item );
4185 #ifdef VIK_CONFIG_OPENSTREETMAP
4186 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4189 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4190 gtk_widget_show ( item );
4193 GtkWidget *delete_submenu = gtk_menu_new ();
4194 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4196 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4197 gtk_widget_show ( item );
4198 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4200 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4201 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4203 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4204 gtk_widget_show ( item );
4206 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4209 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4210 gtk_widget_show ( item );
4212 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4215 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4216 gtk_widget_show ( item );
4218 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4221 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4222 gtk_widget_show ( item );
4224 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4225 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4227 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4228 gtk_widget_show ( item );
4230 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4233 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4234 gtk_widget_show ( item );
4236 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4237 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4239 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4240 gtk_widget_show ( item );
4243 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4244 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4246 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4247 gtk_widget_show ( item );
4250 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4251 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4253 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4254 gtk_widget_show ( item );
4255 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4258 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4260 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4261 gtk_widget_show ( item );
4262 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4265 // Fake Waypoint UUIDs vi simple increasing integer
4266 static guint wp_uuid = 0;
4268 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4272 vik_waypoint_set_name (wp, name);
4274 if ( VIK_LAYER(vtl)->realized )
4276 // Do we need to create the sublayer:
4277 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4278 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4281 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4283 // Visibility column always needed for waypoints
4284 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 );
4286 // Actual setting of visibility dependent on the waypoint
4287 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4289 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4291 // Sort now as post_read is not called on a realized waypoint
4292 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4295 highest_wp_number_add_wp(vtl, name);
4296 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4300 // Fake Track UUIDs vi simple increasing integer
4301 static guint tr_uuid = 0;
4303 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4307 vik_track_set_name (t, name);
4309 if ( VIK_LAYER(vtl)->realized )
4311 // Do we need to create the sublayer:
4312 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4313 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4316 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4317 // Visibility column always needed for tracks
4318 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 );
4320 // Actual setting of visibility dependent on the track
4321 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4323 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4325 // Sort now as post_read is not called on a realized track
4326 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4329 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4331 trw_layer_update_treeview ( vtl, t );
4334 // Fake Route UUIDs vi simple increasing integer
4335 static guint rt_uuid = 0;
4337 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4341 vik_track_set_name (t, name);
4343 if ( VIK_LAYER(vtl)->realized )
4345 // Do we need to create the sublayer:
4346 if ( g_hash_table_size (vtl->routes) == 0 ) {
4347 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4350 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4351 // Visibility column always needed for routes
4352 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 );
4353 // Actual setting of visibility dependent on the route
4354 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4356 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4358 // Sort now as post_read is not called on a realized route
4359 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4362 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4364 trw_layer_update_treeview ( vtl, t );
4367 /* to be called whenever a track has been deleted or may have been changed. */
4368 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4370 if (vtl->current_tp_track == trk )
4371 trw_layer_cancel_current_tp ( vtl, FALSE );
4375 * Normally this is done to due the waypoint size preference having changed
4377 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4379 GHashTableIter iter;
4380 gpointer key, value;
4383 g_hash_table_iter_init ( &iter, vtl->waypoints );
4384 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4385 VikWaypoint *wp = VIK_WAYPOINT(value);
4387 // Reapply symbol setting to update the pixbuf
4388 gchar *tmp_symbol = g_strdup ( wp->symbol );
4389 vik_waypoint_set_symbol ( wp, tmp_symbol );
4390 g_free ( tmp_symbol );
4396 * trw_layer_new_unique_sublayer_name:
4398 * Allocates a unique new name
4400 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4403 gchar *newname = g_strdup(name);
4408 switch ( sublayer_type ) {
4409 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4410 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4412 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4413 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4416 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4419 // If found a name already in use try adding 1 to it and we try again
4421 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4423 newname = new_newname;
4426 } while ( id != NULL);
4431 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4433 // No more uniqueness of name forced when loading from a file
4434 // This now makes this function a little redunant as we just flow the parameters through
4435 vik_trw_layer_add_waypoint ( vtl, name, wp );
4438 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4440 if ( vtl->route_finder_append && vtl->current_track ) {
4441 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4443 // enforce end of current track equal to start of tr
4444 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4445 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4446 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4447 vik_track_add_trackpoint ( vtl->current_track,
4448 vik_trackpoint_copy ( cur_end ),
4452 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4453 vik_track_free ( tr );
4454 vtl->route_finder_append = FALSE; /* this means we have added it */
4457 // No more uniqueness of name forced when loading from a file
4459 vik_trw_layer_add_route ( vtl, name, tr );
4461 vik_trw_layer_add_track ( vtl, name, tr );
4463 if ( vtl->route_finder_check_added_track ) {
4464 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4465 vtl->route_finder_added_track = tr;
4470 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4472 *l = g_list_append(*l, id);
4476 * Move an item from one TRW layer to another TRW layer
4478 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4480 // TODO reconsider strategy when moving within layer (if anything...)
4481 gboolean rename = ( vtl_src != vtl_dest );
4485 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4486 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4490 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4492 newname = g_strdup ( trk->name );
4494 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4495 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4497 vik_trw_layer_delete_track ( vtl_src, trk );
4500 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4501 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4505 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4507 newname = g_strdup ( trk->name );
4509 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4510 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4512 vik_trw_layer_delete_route ( vtl_src, trk );
4515 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4516 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4520 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4522 newname = g_strdup ( wp->name );
4524 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4525 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4527 trw_layer_delete_waypoint ( vtl_src, wp );
4529 // Recalculate bounds even if not renamed as maybe dragged between layers
4530 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4531 trw_layer_calculate_bounds_waypoints ( vtl_src );
4535 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4537 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4538 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4540 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4541 GList *items = NULL;
4544 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4545 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4547 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4548 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4550 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4551 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4556 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4557 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4558 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4559 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4561 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4568 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4569 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4573 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4575 trku_udata *user_data = udata;
4576 if ( trk == user_data->trk ) {
4577 user_data->uuid = id;
4583 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4585 gboolean was_visible = FALSE;
4586 if ( trk && trk->name ) {
4588 if ( trk == vtl->current_track ) {
4589 vtl->current_track = NULL;
4590 vtl->current_tp_track = NULL;
4591 vtl->current_tp_id = NULL;
4592 vtl->moving_tp = FALSE;
4593 vtl->route_finder_started = FALSE;
4596 was_visible = trk->visible;
4598 if ( trk == vtl->route_finder_added_track )
4599 vtl->route_finder_added_track = NULL;
4605 // Hmmm, want key of it
4606 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4608 if ( trkf && udata.uuid ) {
4609 /* could be current_tp, so we have to check */
4610 trw_layer_cancel_tps_of_track ( vtl, trk );
4612 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4615 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4616 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4617 g_hash_table_remove ( vtl->tracks, udata.uuid );
4619 // If last sublayer, then remove sublayer container
4620 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4621 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4624 // Incase it was selected (no item delete signal ATM)
4625 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4631 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4633 gboolean was_visible = FALSE;
4635 if ( trk && trk->name ) {
4637 if ( trk == vtl->current_track ) {
4638 vtl->current_track = NULL;
4639 vtl->current_tp_track = NULL;
4640 vtl->current_tp_id = NULL;
4641 vtl->moving_tp = FALSE;
4644 was_visible = trk->visible;
4646 if ( trk == vtl->route_finder_added_track )
4647 vtl->route_finder_added_track = NULL;
4653 // Hmmm, want key of it
4654 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4656 if ( trkf && udata.uuid ) {
4657 /* could be current_tp, so we have to check */
4658 trw_layer_cancel_tps_of_track ( vtl, trk );
4660 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4663 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4664 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4665 g_hash_table_remove ( vtl->routes, udata.uuid );
4667 // If last sublayer, then remove sublayer container
4668 if ( g_hash_table_size (vtl->routes) == 0 ) {
4669 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4672 // Incase it was selected (no item delete signal ATM)
4673 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4679 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4681 gboolean was_visible = FALSE;
4683 if ( wp && wp->name ) {
4685 if ( wp == vtl->current_wp ) {
4686 vtl->current_wp = NULL;
4687 vtl->current_wp_id = NULL;
4688 vtl->moving_wp = FALSE;
4691 was_visible = wp->visible;
4697 // Hmmm, want key of it
4698 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4700 if ( wpf && udata.uuid ) {
4701 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4704 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4705 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4707 highest_wp_number_remove_wp(vtl, wp->name);
4708 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4710 // If last sublayer, then remove sublayer container
4711 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4712 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4715 // Incase it was selected (no item delete signal ATM)
4716 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4724 // Only for temporary use by trw_layer_delete_waypoint_by_name
4725 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4727 wpu_udata *user_data = udata;
4728 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4729 user_data->uuid = id;
4736 * Delete a waypoint by the given name
4737 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4738 * as there be multiple waypoints with the same name
4740 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4743 // Fake a waypoint with the given name
4744 udata.wp = vik_waypoint_new ();
4745 vik_waypoint_set_name (udata.wp, name);
4746 // Currently only the name is used in this waypoint find function
4749 // Hmmm, want key of it
4750 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4752 vik_waypoint_free (udata.wp);
4754 if ( wpf && udata.uuid )
4755 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4761 VikTrack *trk; // input
4762 gpointer uuid; // output
4765 // Only for temporary use by trw_layer_delete_track_by_name
4766 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4768 tpu_udata *user_data = udata;
4769 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4770 user_data->uuid = id;
4777 * Delete a track by the given name
4778 * NOTE: ATM this will delete the first encountered Track with the specified name
4779 * as there may be multiple tracks with the same name within the specified hash table
4781 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4784 // Fake a track with the given name
4785 udata.trk = vik_track_new ();
4786 vik_track_set_name (udata.trk, name);
4787 // Currently only the name is used in this waypoint find function
4790 // Hmmm, want key of it
4791 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4793 vik_track_free (udata.trk);
4795 if ( trkf && udata.uuid ) {
4796 // This could be a little better written...
4797 if ( vtl->tracks == ht_tracks )
4798 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4799 if ( vtl->routes == ht_tracks )
4800 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4807 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4809 vik_treeview_item_delete (vt, it );
4812 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4815 vtl->current_track = NULL;
4816 vtl->route_finder_added_track = NULL;
4817 if (vtl->current_tp_track)
4818 trw_layer_cancel_current_tp(vtl, FALSE);
4820 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4821 g_hash_table_remove_all(vtl->routes_iters);
4822 g_hash_table_remove_all(vtl->routes);
4824 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4826 vik_layer_emit_update ( VIK_LAYER(vtl) );
4829 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4832 vtl->current_track = NULL;
4833 vtl->route_finder_added_track = NULL;
4834 if (vtl->current_tp_track)
4835 trw_layer_cancel_current_tp(vtl, FALSE);
4837 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4838 g_hash_table_remove_all(vtl->tracks_iters);
4839 g_hash_table_remove_all(vtl->tracks);
4841 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4843 vik_layer_emit_update ( VIK_LAYER(vtl) );
4846 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4848 vtl->current_wp = NULL;
4849 vtl->current_wp_id = NULL;
4850 vtl->moving_wp = FALSE;
4852 highest_wp_number_reset(vtl);
4854 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4855 g_hash_table_remove_all(vtl->waypoints_iters);
4856 g_hash_table_remove_all(vtl->waypoints);
4858 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4860 vik_layer_emit_update ( VIK_LAYER(vtl) );
4863 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4865 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4866 // Get confirmation from the user
4867 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4868 _("Are you sure you want to delete all tracks in %s?"),
4869 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4870 vik_trw_layer_delete_all_tracks (vtl);
4873 static void trw_layer_delete_all_routes ( menu_array_layer values )
4875 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4876 // Get confirmation from the user
4877 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4878 _("Are you sure you want to delete all routes in %s?"),
4879 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4880 vik_trw_layer_delete_all_routes (vtl);
4883 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4885 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4886 // Get confirmation from the user
4887 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4888 _("Are you sure you want to delete all waypoints in %s?"),
4889 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4890 vik_trw_layer_delete_all_waypoints (vtl);
4893 static void trw_layer_delete_item ( menu_array_sublayer values )
4895 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4896 gboolean was_visible = FALSE;
4897 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4899 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4900 if ( wp && wp->name ) {
4901 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4902 // Get confirmation from the user
4903 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4904 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4905 _("Are you sure you want to delete the waypoint \"%s\"?"),
4908 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4911 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4913 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4914 if ( trk && trk->name ) {
4915 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4916 // Get confirmation from the user
4917 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4918 _("Are you sure you want to delete the track \"%s\"?"),
4921 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4926 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4927 if ( trk && trk->name ) {
4928 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4929 // Get confirmation from the user
4930 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4931 _("Are you sure you want to delete the route \"%s\"?"),
4934 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4938 vik_layer_emit_update ( VIK_LAYER(vtl) );
4942 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4944 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4946 vik_waypoint_set_name ( wp, new_name );
4948 // Now update the treeview as well
4953 // Need key of it for treeview update
4954 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4956 if ( wpf && udataU.uuid ) {
4957 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4960 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4961 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4967 * Maintain icon of waypoint in the treeview
4969 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4971 // update the treeview
4976 // Need key of it for treeview update
4977 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4979 if ( wpf && udataU.uuid ) {
4980 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4983 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4988 static void trw_layer_properties_item ( menu_array_sublayer values )
4990 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4991 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4993 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4995 if ( wp && wp->name )
4997 gboolean updated = FALSE;
4998 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5000 trw_layer_waypoint_rename ( vtl, wp, new_name );
5002 if ( updated && values[MA_TV_ITER] )
5003 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5005 if ( updated && VIK_LAYER(vtl)->visible )
5006 vik_layer_emit_update ( VIK_LAYER(vtl) );
5012 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5013 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5015 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5017 if ( tr && tr->name )
5019 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5030 * trw_layer_track_statistics:
5032 * Show track statistics.
5033 * ATM jump to the stats page in the properties
5034 * TODO: consider separating the stats into an individual dialog?
5036 static void trw_layer_track_statistics ( menu_array_sublayer values )
5038 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5040 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5041 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5043 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5045 if ( trk && trk->name ) {
5046 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5056 * Update the treeview of the track id - primarily to update the icon
5058 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5064 gpointer *trkf = NULL;
5065 if ( trk->is_route )
5066 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5068 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5070 if ( trkf && udata.uuid ) {
5072 GtkTreeIter *iter = NULL;
5073 if ( trk->is_route )
5074 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5076 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5079 // TODO: Make this a function
5080 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5081 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5082 ((trk->color.green & 0xff00) << 8) |
5083 (trk->color.blue & 0xff00);
5084 gdk_pixbuf_fill ( pixbuf, pixel );
5085 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5086 g_object_unref (pixbuf);
5093 Parameter 1 -> VikLayersPanel
5094 Parameter 2 -> VikLayer
5095 Parameter 3 -> VikViewport
5097 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5100 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5101 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5104 /* since vlp not set, vl & vvp should be valid instead! */
5106 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5107 vik_layer_emit_update ( VIK_LAYER(vl) );
5112 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5114 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5116 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5117 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5119 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5121 if ( track && track->trackpoints )
5122 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5125 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5127 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5129 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5130 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5132 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5134 if ( track && track->trackpoints )
5136 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5138 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5139 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5140 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5141 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5142 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5146 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5148 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5150 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5151 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5153 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5158 // Converting a track to a route can be a bit more complicated,
5159 // so give a chance to change our minds:
5160 if ( !trk->is_route &&
5161 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5162 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5164 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5165 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5170 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5173 trk_copy->is_route = !trk_copy->is_route;
5175 // ATM can't set name to self - so must create temporary copy
5176 gchar *name = g_strdup ( trk_copy->name );
5178 // Delete old one and then add new one
5179 if ( trk->is_route ) {
5180 vik_trw_layer_delete_route ( vtl, trk );
5181 vik_trw_layer_add_track ( vtl, name, trk_copy );
5184 // Extra route conversion bits...
5185 vik_track_merge_segments ( trk_copy );
5186 vik_track_to_routepoints ( trk_copy );
5188 vik_trw_layer_delete_track ( vtl, trk );
5189 vik_trw_layer_add_route ( vtl, name, trk_copy );
5193 // Update in case color of track / route changes when moving between sublayers
5194 vik_layer_emit_update ( VIK_LAYER(vtl) );
5197 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5199 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5201 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5202 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5204 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5207 vik_track_anonymize_times ( track );
5210 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5212 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5214 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5215 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5217 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5222 vtl->current_track = track;
5223 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);
5225 if ( track->trackpoints )
5226 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5230 * extend a track using route finder
5232 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5234 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5235 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5239 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5240 vtl->current_track = track;
5241 vtl->route_finder_started = TRUE;
5243 if ( track->trackpoints )
5244 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5250 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5252 // If have a vlp then perform a basic test to see if any DEM info available...
5254 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5256 if ( !g_list_length(dems) ) {
5257 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5265 * apply_dem_data_common:
5267 * A common function for applying the DEM values and reporting the results.
5269 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5271 if ( !trw_layer_dem_test ( vtl, vlp ) )
5274 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5275 // Inform user how much was changed
5277 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5278 g_snprintf(str, 64, tmp_str, changed);
5279 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5282 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5284 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5286 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5287 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5289 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5292 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5295 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5297 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5299 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5300 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5302 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5305 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5311 * A common function for applying the elevation smoothing and reporting the results.
5313 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5315 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5316 // Inform user how much was changed
5318 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5319 g_snprintf(str, 64, tmp_str, changed);
5320 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5326 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5328 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5330 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5331 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5333 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5338 smooth_it ( vtl, track, FALSE );
5341 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5343 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5345 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5346 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5348 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5353 smooth_it ( vtl, track, TRUE );
5357 * Commonal helper function
5359 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5362 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5363 g_snprintf(str, 64, tmp_str, changed);
5364 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5367 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5369 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5370 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5372 if ( !trw_layer_dem_test ( vtl, vlp ) )
5376 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5378 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5380 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5384 GHashTableIter iter;
5385 gpointer key, value;
5387 g_hash_table_iter_init ( &iter, vtl->waypoints );
5388 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5389 VikWaypoint *wp = VIK_WAYPOINT(value);
5390 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5393 wp_changed_message ( vtl, changed );
5396 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5398 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5399 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5401 if ( !trw_layer_dem_test ( vtl, vlp ) )
5405 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5407 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5409 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5413 GHashTableIter iter;
5414 gpointer key, value;
5416 g_hash_table_iter_init ( &iter, vtl->waypoints );
5417 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5418 VikWaypoint *wp = VIK_WAYPOINT(value);
5419 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5422 wp_changed_message ( vtl, changed );
5425 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5427 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5429 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5430 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5432 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5436 if ( !track->trackpoints )
5438 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5441 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5443 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5445 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5446 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5448 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5453 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5456 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5459 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5461 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5463 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5464 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5466 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5471 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5474 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5477 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5479 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5481 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5482 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5484 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5489 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5492 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5496 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5498 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5500 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5502 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5503 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5505 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5507 if ( trk && trk->trackpoints )
5509 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5510 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5511 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5512 if ( values[MA_VLP] )
5513 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5515 vik_layer_emit_update ( VIK_LAYER(vtl) );
5520 * Refine the selected track/route with a routing engine.
5521 * The routing engine is selected by the user, when requestiong the job.
5523 static void trw_layer_route_refine ( menu_array_sublayer values )
5525 static gint last_engine = 0;
5526 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5529 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5530 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5532 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5534 if ( trk && trk->trackpoints )
5536 /* Check size of the route */
5537 int nb = vik_track_get_tp_count(trk);
5539 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5540 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5541 GTK_MESSAGE_WARNING,
5542 GTK_BUTTONS_OK_CANCEL,
5543 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5545 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5546 gtk_widget_destroy ( dialog );
5547 if (response != GTK_RESPONSE_OK )
5550 /* Select engine from dialog */
5551 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5552 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5553 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5555 GTK_RESPONSE_REJECT,
5557 GTK_RESPONSE_ACCEPT,
5559 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5560 gtk_widget_show_all(label);
5562 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5564 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5565 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5566 gtk_widget_show_all(combo);
5568 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5570 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5572 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5574 /* Dialog validated: retrieve selected engine and do the job */
5575 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5576 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5579 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5581 /* Force saving track */
5582 /* FIXME: remove or rename this hack */
5583 vtl->route_finder_check_added_track = TRUE;
5586 vik_routing_engine_refine (routing, vtl, trk);
5588 /* FIXME: remove or rename this hack */
5589 if ( vtl->route_finder_added_track )
5590 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5592 vtl->route_finder_added_track = NULL;
5593 vtl->route_finder_check_added_track = FALSE;
5595 vik_layer_emit_update ( VIK_LAYER(vtl) );
5597 /* Restore cursor */
5598 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5600 gtk_widget_destroy ( dialog );
5604 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5606 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5607 trw_layer_tpwin_init ( vtl );
5610 /*************************************
5611 * merge/split by time routines
5612 *************************************/
5614 /* called for each key in track hash table.
5615 * If the current track has the same time stamp type, add it to the result,
5616 * except the one pointed by "exclude".
5617 * set exclude to NULL if there is no exclude to check.
5618 * Note that the result is in reverse (for performance reasons).
5623 gboolean with_timestamps;
5625 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5627 twt_udata *user_data = udata;
5628 VikTrackpoint *p1, *p2;
5629 VikTrack *trk = VIK_TRACK(value);
5630 if (trk == user_data->exclude) {
5634 if (trk->trackpoints) {
5635 p1 = vik_track_get_tp_first(trk);
5636 p2 = vik_track_get_tp_last(trk);
5638 if ( user_data->with_timestamps ) {
5639 if (!p1->has_timestamp || !p2->has_timestamp) {
5644 // Don't add tracks with timestamps when getting non timestamp tracks
5645 if (p1->has_timestamp || p2->has_timestamp) {
5651 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5655 * find_nearby_tracks_by_time:
5657 * Called for each track in track hash table.
5658 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5659 * to the current track, then the current track is added to the list in user_data[0]
5661 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5663 VikTrack *trk = VIK_TRACK(value);
5665 GList **nearby_tracks = ((gpointer *)user_data)[0];
5666 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5668 if ( !orig_trk || !orig_trk->trackpoints )
5672 * detect reasons for not merging, and return
5673 * if no reason is found not to merge, then do it.
5676 twt_udata *udata = user_data;
5677 // Exclude the original track from the compiled list
5678 if (trk == udata->exclude) {
5682 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5683 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5685 if (trk->trackpoints) {
5687 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5688 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5690 if (!p1->has_timestamp || !p2->has_timestamp) {
5691 //g_print("no timestamp\n");
5695 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5696 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5697 if (! (abs(t1 - p2->timestamp) < threshold ||
5699 abs(p1->timestamp - t2) < threshold)
5706 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5709 /* comparison function used to sort tracks; a and b are hash table keys */
5710 /* Not actively used - can be restored if needed
5711 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5713 GHashTable *tracks = user_data;
5716 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5717 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5719 if (t1 < t2) return -1;
5720 if (t1 > t2) return 1;
5725 /* comparison function used to sort trackpoints */
5726 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5728 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5730 if (t1 < t2) return -1;
5731 if (t1 > t2) return 1;
5736 * comparison function which can be used to sort tracks or waypoints by name
5738 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5740 const gchar* namea = (const gchar*) a;
5741 const gchar* nameb = (const gchar*) b;
5742 if ( namea == NULL || nameb == NULL)
5745 // Same sort method as used in the vik_treeview_*_alphabetize functions
5746 return strcmp ( namea, nameb );
5750 * Attempt to merge selected track with other tracks specified by the user
5751 * Tracks to merge with must be of the same 'type' as the selected track -
5752 * either all with timestamps, or all without timestamps
5754 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5756 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5757 GList *other_tracks = NULL;
5758 GHashTable *ght_tracks;
5759 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5760 ght_tracks = vtl->routes;
5762 ght_tracks = vtl->tracks;
5764 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5769 if ( !track->trackpoints )
5773 udata.result = &other_tracks;
5774 udata.exclude = track;
5775 // Allow merging with 'similar' time type time tracks
5776 // i.e. either those times, or those without
5777 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5779 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5780 other_tracks = g_list_reverse(other_tracks);
5782 if ( !other_tracks ) {
5783 if ( udata.with_timestamps )
5784 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5786 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5790 // Sort alphabetically for user presentation
5791 // Convert into list of names for usage with dialog function
5792 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5793 GList *other_tracks_names = NULL;
5794 GList *iter = g_list_first ( other_tracks );
5796 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5797 iter = g_list_next ( iter );
5800 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5802 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5806 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5807 g_list_free(other_tracks);
5808 g_list_free(other_tracks_names);
5813 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5814 VikTrack *merge_track;
5815 if ( track->is_route )
5816 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5818 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5821 vik_track_steal_and_append_trackpoints ( track, merge_track );
5822 if ( track->is_route )
5823 vik_trw_layer_delete_route (vtl, merge_track);
5825 vik_trw_layer_delete_track (vtl, merge_track);
5826 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5829 for (l = merge_list; l != NULL; l = g_list_next(l))
5831 g_list_free(merge_list);
5833 vik_layer_emit_update( VIK_LAYER(vtl) );
5837 // c.f. trw_layer_sorted_track_id_by_name_list
5838 // but don't add the specified track to the list (normally current track)
5839 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5841 twt_udata *user_data = udata;
5844 if (trk == user_data->exclude) {
5848 // Sort named list alphabetically
5849 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5853 * Join - this allows combining 'tracks' and 'track routes'
5854 * i.e. doesn't care about whether tracks have consistent timestamps
5855 * ATM can only append one track at a time to the currently selected track
5857 static void trw_layer_append_track ( menu_array_sublayer values )
5860 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5862 GHashTable *ght_tracks;
5863 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5864 ght_tracks = vtl->routes;
5866 ght_tracks = vtl->tracks;
5868 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5873 GList *other_tracks_names = NULL;
5875 // Sort alphabetically for user presentation
5876 // Convert into list of names for usage with dialog function
5877 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5879 udata.result = &other_tracks_names;
5880 udata.exclude = trk;
5882 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5884 // Note the limit to selecting one track only
5885 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5886 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5887 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5890 trk->is_route ? _("Append Route"): _("Append Track"),
5891 trk->is_route ? _("Select the route to append after the current route") :
5892 _("Select the track to append after the current track") );
5894 g_list_free(other_tracks_names);
5896 // It's a list, but shouldn't contain more than one other track!
5897 if ( append_list ) {
5899 for (l = append_list; l != NULL; l = g_list_next(l)) {
5900 // TODO: at present this uses the first track found by name,
5901 // which with potential multiple same named tracks may not be the one selected...
5902 VikTrack *append_track;
5903 if ( trk->is_route )
5904 append_track = vik_trw_layer_get_route ( vtl, l->data );
5906 append_track = vik_trw_layer_get_track ( vtl, l->data );
5908 if ( append_track ) {
5909 vik_track_steal_and_append_trackpoints ( trk, append_track );
5910 if ( trk->is_route )
5911 vik_trw_layer_delete_route (vtl, append_track);
5913 vik_trw_layer_delete_track (vtl, append_track);
5916 for (l = append_list; l != NULL; l = g_list_next(l))
5918 g_list_free(append_list);
5920 vik_layer_emit_update( VIK_LAYER(vtl) );
5925 * Very similar to trw_layer_append_track for joining
5926 * but this allows selection from the 'other' list
5927 * If a track is selected, then is shows routes and joins the selected one
5928 * If a route is selected, then is shows tracks and joins the selected one
5930 static void trw_layer_append_other ( menu_array_sublayer values )
5933 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5935 GHashTable *ght_mykind, *ght_others;
5936 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5937 ght_mykind = vtl->routes;
5938 ght_others = vtl->tracks;
5941 ght_mykind = vtl->tracks;
5942 ght_others = vtl->routes;
5945 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5950 GList *other_tracks_names = NULL;
5952 // Sort alphabetically for user presentation
5953 // Convert into list of names for usage with dialog function
5954 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5956 udata.result = &other_tracks_names;
5957 udata.exclude = trk;
5959 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5961 // Note the limit to selecting one track only
5962 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5963 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5964 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5967 trk->is_route ? _("Append Track"): _("Append Route"),
5968 trk->is_route ? _("Select the track to append after the current route") :
5969 _("Select the route to append after the current track") );
5971 g_list_free(other_tracks_names);
5973 // It's a list, but shouldn't contain more than one other track!
5974 if ( append_list ) {
5976 for (l = append_list; l != NULL; l = g_list_next(l)) {
5977 // TODO: at present this uses the first track found by name,
5978 // which with potential multiple same named tracks may not be the one selected...
5980 // Get FROM THE OTHER TYPE list
5981 VikTrack *append_track;
5982 if ( trk->is_route )
5983 append_track = vik_trw_layer_get_track ( vtl, l->data );
5985 append_track = vik_trw_layer_get_route ( vtl, l->data );
5987 if ( append_track ) {
5989 if ( !append_track->is_route &&
5990 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5991 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5993 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5994 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5995 vik_track_merge_segments ( append_track );
5996 vik_track_to_routepoints ( append_track );
6003 vik_track_steal_and_append_trackpoints ( trk, append_track );
6005 // Delete copied which is FROM THE OTHER TYPE list
6006 if ( trk->is_route )
6007 vik_trw_layer_delete_track (vtl, append_track);
6009 vik_trw_layer_delete_route (vtl, append_track);
6012 for (l = append_list; l != NULL; l = g_list_next(l))
6014 g_list_free(append_list);
6015 vik_layer_emit_update( VIK_LAYER(vtl) );
6019 /* merge by segments */
6020 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6022 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6023 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6024 guint segments = vik_track_merge_segments ( trk );
6025 // NB currently no need to redraw as segments not actually shown on the display
6026 // However inform the user of what happened:
6028 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6029 g_snprintf(str, 64, tmp_str, segments);
6030 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6033 /* merge by time routine */
6034 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6036 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6040 GList *tracks_with_timestamp = NULL;
6041 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6042 if (orig_trk->trackpoints &&
6043 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6044 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6049 udata.result = &tracks_with_timestamp;
6050 udata.exclude = orig_trk;
6051 udata.with_timestamps = TRUE;
6052 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6053 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6055 if (!tracks_with_timestamp) {
6056 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6059 g_list_free(tracks_with_timestamp);
6061 static guint threshold_in_minutes = 1;
6062 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6063 _("Merge Threshold..."),
6064 _("Merge when time between tracks less than:"),
6065 &threshold_in_minutes)) {
6069 // keep attempting to merge all tracks until no merges within the time specified is possible
6070 gboolean attempt_merge = TRUE;
6071 GList *nearby_tracks = NULL;
6073 static gpointer params[3];
6075 while ( attempt_merge ) {
6077 // Don't try again unless tracks have changed
6078 attempt_merge = FALSE;
6080 trps = orig_trk->trackpoints;
6084 if (nearby_tracks) {
6085 g_list_free(nearby_tracks);
6086 nearby_tracks = NULL;
6089 params[0] = &nearby_tracks;
6090 params[1] = orig_trk;
6091 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6093 /* get a list of adjacent-in-time tracks */
6094 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6097 GList *l = nearby_tracks;
6099 /* remove trackpoints from merged track, delete track */
6100 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6101 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6103 // Tracks have changed, therefore retry again against all the remaining tracks
6104 attempt_merge = TRUE;
6109 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6112 g_list_free(nearby_tracks);
6114 vik_layer_emit_update( VIK_LAYER(vtl) );
6118 * Split a track at the currently selected trackpoint
6120 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6122 if ( !vtl->current_tpl )
6125 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6126 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6128 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6129 GList *newglist = g_list_alloc ();
6130 newglist->prev = NULL;
6131 newglist->next = vtl->current_tpl->next;
6132 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6133 tr->trackpoints = newglist;
6135 vtl->current_tpl->next->prev = newglist; /* end old track here */
6136 vtl->current_tpl->next = NULL;
6138 // Bounds of the selected track changed due to the split
6139 vik_track_calculate_bounds ( vtl->current_tp_track );
6141 vtl->current_tpl = newglist; /* change tp to first of new track. */
6142 vtl->current_tp_track = tr;
6145 vik_trw_layer_add_route ( vtl, name, tr );
6147 vik_trw_layer_add_track ( vtl, name, tr );
6149 // Bounds of the new track created by the split
6150 vik_track_calculate_bounds ( tr );
6156 // Also need id of newly created track
6159 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6161 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6163 if ( trkf && udata.uuid )
6164 vtl->current_tp_id = udata.uuid;
6166 vtl->current_tp_id = NULL;
6168 vik_layer_emit_update(VIK_LAYER(vtl));
6174 /* split by time routine */
6175 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6177 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6178 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6179 GList *trps = track->trackpoints;
6181 GList *newlists = NULL;
6182 GList *newtps = NULL;
6183 static guint thr = 1;
6190 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6191 _("Split Threshold..."),
6192 _("Split when time between trackpoints exceeds:"),
6197 /* iterate through trackpoints, and copy them into new lists without touching original list */
6198 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6202 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6204 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6207 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6208 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6209 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6211 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6216 if (ts - prev_ts > thr*60) {
6217 /* flush accumulated trackpoints into new list */
6218 newlists = g_list_append(newlists, g_list_reverse(newtps));
6222 /* accumulate trackpoint copies in newtps, in reverse order */
6223 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6225 iter = g_list_next(iter);
6228 newlists = g_list_append(newlists, g_list_reverse(newtps));
6231 /* put lists of trackpoints into tracks */
6233 // Only bother updating if the split results in new tracks
6234 if (g_list_length (newlists) > 1) {
6239 tr = vik_track_copy ( track, FALSE );
6240 tr->trackpoints = (GList *)(iter->data);
6242 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6243 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6244 g_free ( new_tr_name );
6245 vik_track_calculate_bounds ( tr );
6246 iter = g_list_next(iter);
6248 // Remove original track and then update the display
6249 vik_trw_layer_delete_track (vtl, track);
6250 vik_layer_emit_update(VIK_LAYER(vtl));
6252 g_list_free(newlists);
6256 * Split a track by the number of points as specified by the user
6258 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6260 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6262 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6263 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6265 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6270 // Check valid track
6271 GList *trps = track->trackpoints;
6275 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6276 _("Split Every Nth Point"),
6277 _("Split on every Nth point:"),
6278 250, // Default value as per typical limited track capacity of various GPS devices
6282 // Was a valid number returned?
6288 GList *newlists = NULL;
6289 GList *newtps = NULL;
6294 /* accumulate trackpoint copies in newtps, in reverse order */
6295 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6297 if (count >= points) {
6298 /* flush accumulated trackpoints into new list */
6299 newlists = g_list_append(newlists, g_list_reverse(newtps));
6303 iter = g_list_next(iter);
6306 // If there is a remaining chunk put that into the new split list
6307 // This may well be the whole track if no split points were encountered
6309 newlists = g_list_append(newlists, g_list_reverse(newtps));
6312 /* put lists of trackpoints into tracks */
6314 // Only bother updating if the split results in new tracks
6315 if (g_list_length (newlists) > 1) {
6320 tr = vik_track_copy ( track, FALSE );
6321 tr->trackpoints = (GList *)(iter->data);
6323 if ( track->is_route ) {
6324 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6325 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6328 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6329 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6331 g_free ( new_tr_name );
6332 vik_track_calculate_bounds ( tr );
6334 iter = g_list_next(iter);
6336 // Remove original track and then update the display
6337 if ( track->is_route )
6338 vik_trw_layer_delete_route (vtl, track);
6340 vik_trw_layer_delete_track (vtl, track);
6341 vik_layer_emit_update(VIK_LAYER(vtl));
6343 g_list_free(newlists);
6347 * Split a track at the currently selected trackpoint
6349 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6351 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6352 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6353 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6357 * Split a track by its segments
6358 * Routes do not have segments so don't call this for routes
6360 static void trw_layer_split_segments ( menu_array_sublayer values )
6362 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6363 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6370 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6373 for ( i = 0; i < ntracks; i++ ) {
6375 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6376 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6377 g_free ( new_tr_name );
6382 // Remove original track
6383 vik_trw_layer_delete_track ( vtl, trk );
6384 vik_layer_emit_update ( VIK_LAYER(vtl) );
6387 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6390 /* end of split/merge routines */
6392 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6396 // Find available adjacent trackpoint
6397 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6398 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6399 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6401 // Delete current trackpoint
6402 vik_trackpoint_free ( vtl->current_tpl->data );
6403 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6405 // Set to current to the available adjacent trackpoint
6406 vtl->current_tpl = new_tpl;
6408 if ( vtl->current_tp_track ) {
6409 vik_track_calculate_bounds ( vtl->current_tp_track );
6413 // Delete current trackpoint
6414 vik_trackpoint_free ( vtl->current_tpl->data );
6415 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6416 trw_layer_cancel_current_tp ( vtl, FALSE );
6421 * Delete the selected point
6423 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6425 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6427 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6428 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6430 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6435 if ( !vtl->current_tpl )
6438 trw_layer_trackpoint_selected_delete ( vtl, trk );
6440 // Track has been updated so update tps:
6441 trw_layer_cancel_tps_of_track ( vtl, trk );
6443 vik_layer_emit_update ( VIK_LAYER(vtl) );
6447 * Delete adjacent track points at the same position
6448 * AKA Delete Dulplicates on the Properties Window
6450 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6452 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6454 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6455 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6457 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6462 gulong removed = vik_track_remove_dup_points ( trk );
6464 // Track has been updated so update tps:
6465 trw_layer_cancel_tps_of_track ( vtl, trk );
6467 // Inform user how much was deleted as it's not obvious from the normal view
6469 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6470 g_snprintf(str, 64, tmp_str, removed);
6471 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6473 vik_layer_emit_update ( VIK_LAYER(vtl) );
6477 * Delete adjacent track points with the same timestamp
6478 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6480 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6482 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6484 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6485 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6487 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6492 gulong removed = vik_track_remove_same_time_points ( trk );
6494 // Track has been updated so update tps:
6495 trw_layer_cancel_tps_of_track ( vtl, trk );
6497 // Inform user how much was deleted as it's not obvious from the normal view
6499 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6500 g_snprintf(str, 64, tmp_str, removed);
6501 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6503 vik_layer_emit_update ( VIK_LAYER(vtl) );
6509 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6511 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6513 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6514 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6516 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6521 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6523 vik_layer_emit_update ( VIK_LAYER(vtl) );
6526 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6528 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6530 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6531 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6533 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6538 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6540 vik_layer_emit_update ( VIK_LAYER(vtl) );
6546 static void trw_layer_reverse ( menu_array_sublayer values )
6548 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6550 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6551 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6553 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6558 vik_track_reverse ( track );
6560 vik_layer_emit_update ( VIK_LAYER(vtl) );
6564 * Open a diary at the specified date
6566 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6569 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6570 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6571 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6572 g_error_free ( err );
6578 * Open a diary at the date of the track or waypoint
6580 static void trw_layer_diary ( menu_array_sublayer values )
6582 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6584 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6585 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6591 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6592 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6593 trw_layer_diary_open ( vtl, date_buf );
6596 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6598 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6599 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6605 if ( wpt->has_timestamp ) {
6606 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6607 trw_layer_diary_open ( vtl, date_buf );
6610 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6615 * Similar to trw_layer_enum_item, but this uses a sorted method
6618 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6620 GList **list = (GList**)udata;
6621 // *list = g_list_prepend(*all, key); //unsorted method
6622 // Sort named list alphabetically
6623 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6628 * Now Waypoint specific sort
6630 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6632 GList **list = (GList**)udata;
6633 // Sort named list alphabetically
6634 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6638 * Track specific sort
6640 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6642 GList **list = (GList**)udata;
6643 // Sort named list alphabetically
6644 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6649 gboolean has_same_track_name;
6650 const gchar *same_track_name;
6651 } same_track_name_udata;
6653 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6655 const gchar* namea = (const gchar*) aa;
6656 const gchar* nameb = (const gchar*) bb;
6659 gint result = strcmp ( namea, nameb );
6661 if ( result == 0 ) {
6662 // Found two names the same
6663 same_track_name_udata *user_data = udata;
6664 user_data->has_same_track_name = TRUE;
6665 user_data->same_track_name = namea;
6668 // Leave ordering the same
6673 * Find out if any tracks have the same name in this hash table
6675 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6677 // Sort items by name, then compare if any next to each other are the same
6679 GList *track_names = NULL;
6680 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6683 if ( ! track_names )
6686 same_track_name_udata udata;
6687 udata.has_same_track_name = FALSE;
6689 // Use sort routine to traverse list comparing items
6690 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6691 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6692 // Still no tracks...
6696 return udata.has_same_track_name;
6700 * Force unqiue track names for the track table specified
6701 * Note the panel is a required parameter to enable the update of the names displayed
6702 * Specify if on tracks or else on routes
6704 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6706 // . Search list for an instance of repeated name
6707 // . get track of this name
6708 // . create new name
6709 // . rename track & update equiv. treeview iter
6710 // . repeat until all different
6712 same_track_name_udata udata;
6714 GList *track_names = NULL;
6715 udata.has_same_track_name = FALSE;
6716 udata.same_track_name = NULL;
6718 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6721 if ( ! track_names )
6724 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6726 // Still no tracks...
6727 if ( ! dummy_list1 )
6730 while ( udata.has_same_track_name ) {
6732 // Find a track with the same name
6735 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6737 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6741 g_critical("Houston, we've had a problem.");
6742 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6743 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6748 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6749 vik_track_set_name ( trk, newname );
6755 // Need want key of it for treeview update
6756 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6758 if ( trkf && udataU.uuid ) {
6762 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6764 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6767 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6769 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6771 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6775 // Start trying to find same names again...
6777 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6778 udata.has_same_track_name = FALSE;
6779 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6781 // No tracks any more - give up searching
6782 if ( ! dummy_list2 )
6783 udata.has_same_track_name = FALSE;
6787 vik_layers_panel_emit_update ( vlp );
6790 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6792 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6795 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6796 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6797 iter = &(vtl->tracks_iter);
6798 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6800 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6801 iter = &(vtl->routes_iter);
6802 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6804 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6805 iter = &(vtl->waypoints_iter);
6806 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6810 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6813 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6815 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6818 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6819 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6820 iter = &(vtl->tracks_iter);
6821 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6823 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6824 iter = &(vtl->routes_iter);
6825 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6827 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6828 iter = &(vtl->waypoints_iter);
6829 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6833 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6839 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6841 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6844 // Ensure list of track names offered is unique
6845 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6846 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6847 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6848 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6854 // Sort list alphabetically for better presentation
6855 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6858 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6862 // Get list of items to delete from the user
6863 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6866 _("Delete Selection"),
6867 _("Select tracks to delete"));
6870 // Delete requested tracks
6871 // since specificly requested, IMHO no need for extra confirmation
6872 if ( delete_list ) {
6874 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6875 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6876 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6878 g_list_free(delete_list);
6879 vik_layer_emit_update( VIK_LAYER(vtl) );
6886 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6888 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6891 // Ensure list of track names offered is unique
6892 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6893 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6894 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6895 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6901 // Sort list alphabetically for better presentation
6902 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6905 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6909 // Get list of items to delete from the user
6910 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6913 _("Delete Selection"),
6914 _("Select routes to delete") );
6917 // Delete requested routes
6918 // since specificly requested, IMHO no need for extra confirmation
6919 if ( delete_list ) {
6921 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6922 // This deletes first route it finds of that name (but uniqueness is enforced above)
6923 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6925 g_list_free(delete_list);
6926 vik_layer_emit_update( VIK_LAYER(vtl) );
6931 gboolean has_same_waypoint_name;
6932 const gchar *same_waypoint_name;
6933 } same_waypoint_name_udata;
6935 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6937 const gchar* namea = (const gchar*) aa;
6938 const gchar* nameb = (const gchar*) bb;
6941 gint result = strcmp ( namea, nameb );
6943 if ( result == 0 ) {
6944 // Found two names the same
6945 same_waypoint_name_udata *user_data = udata;
6946 user_data->has_same_waypoint_name = TRUE;
6947 user_data->same_waypoint_name = namea;
6950 // Leave ordering the same
6955 * Find out if any waypoints have the same name in this layer
6957 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6959 // Sort items by name, then compare if any next to each other are the same
6961 GList *waypoint_names = NULL;
6962 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6965 if ( ! waypoint_names )
6968 same_waypoint_name_udata udata;
6969 udata.has_same_waypoint_name = FALSE;
6971 // Use sort routine to traverse list comparing items
6972 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6973 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6974 // Still no waypoints...
6978 return udata.has_same_waypoint_name;
6982 * Force unqiue waypoint names for this layer
6983 * Note the panel is a required parameter to enable the update of the names displayed
6985 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6987 // . Search list for an instance of repeated name
6988 // . get waypoint of this name
6989 // . create new name
6990 // . rename waypoint & update equiv. treeview iter
6991 // . repeat until all different
6993 same_waypoint_name_udata udata;
6995 GList *waypoint_names = NULL;
6996 udata.has_same_waypoint_name = FALSE;
6997 udata.same_waypoint_name = NULL;
6999 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7002 if ( ! waypoint_names )
7005 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7007 // Still no waypoints...
7008 if ( ! dummy_list1 )
7011 while ( udata.has_same_waypoint_name ) {
7013 // Find a waypoint with the same name
7014 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7018 g_critical("Houston, we've had a problem.");
7019 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7020 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7025 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7027 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7029 // Start trying to find same names again...
7030 waypoint_names = NULL;
7031 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7032 udata.has_same_waypoint_name = FALSE;
7033 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7035 // No waypoints any more - give up searching
7036 if ( ! dummy_list2 )
7037 udata.has_same_waypoint_name = FALSE;
7041 vik_layers_panel_emit_update ( vlp );
7047 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7049 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7052 // Ensure list of waypoint names offered is unique
7053 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7054 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7055 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7056 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7062 // Sort list alphabetically for better presentation
7063 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7065 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7069 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7071 // Get list of items to delete from the user
7072 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7075 _("Delete Selection"),
7076 _("Select waypoints to delete"));
7079 // Delete requested waypoints
7080 // since specificly requested, IMHO no need for extra confirmation
7081 if ( delete_list ) {
7083 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7084 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7085 trw_layer_delete_waypoint_by_name (vtl, l->data);
7087 g_list_free(delete_list);
7089 trw_layer_calculate_bounds_waypoints ( vtl );
7090 vik_layer_emit_update( VIK_LAYER(vtl) );
7098 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7100 vik_treeview_item_toggle_visible ( vt, it );
7106 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7108 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7114 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7116 wp->visible = GPOINTER_TO_INT (on_off);
7122 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7124 wp->visible = !wp->visible;
7130 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7132 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7133 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7134 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7135 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7137 vik_layer_emit_update ( VIK_LAYER(vtl) );
7143 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7145 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7146 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7147 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7148 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7150 vik_layer_emit_update ( VIK_LAYER(vtl) );
7156 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7158 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7159 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7160 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7162 vik_layer_emit_update ( VIK_LAYER(vtl) );
7168 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7170 trk->visible = GPOINTER_TO_INT (on_off);
7176 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7178 trk->visible = !trk->visible;
7184 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7186 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7187 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7188 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7189 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7191 vik_layer_emit_update ( VIK_LAYER(vtl) );
7197 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7199 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7200 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7201 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7202 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7204 vik_layer_emit_update ( VIK_LAYER(vtl) );
7210 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7212 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7213 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7214 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7216 vik_layer_emit_update ( VIK_LAYER(vtl) );
7222 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7224 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7225 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7226 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7227 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7229 vik_layer_emit_update ( VIK_LAYER(vtl) );
7235 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7237 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7238 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7239 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7240 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7242 vik_layer_emit_update ( VIK_LAYER(vtl) );
7248 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7250 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7251 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7252 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7254 vik_layer_emit_update ( VIK_LAYER(vtl) );
7258 * vik_trw_layer_build_waypoint_list_t:
7260 * Helper function to construct a list of #vik_trw_waypoint_list_t
7262 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7264 GList *waypoints_and_layers = NULL;
7265 // build waypoints_and_layers list
7266 while ( waypoints ) {
7267 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7268 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7270 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7271 waypoints = g_list_next ( waypoints );
7273 return waypoints_and_layers;
7277 * trw_layer_create_waypoint_list:
7279 * Create the latest list of waypoints with the associated layer(s)
7280 * Although this will always be from a single layer here
7282 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7284 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7285 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7287 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7291 * trw_layer_analyse_close:
7293 * Stuff to do on dialog closure
7295 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7297 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7298 gtk_widget_destroy ( dialog );
7299 vtl->tracks_analysis_dialog = NULL;
7303 * vik_trw_layer_build_track_list_t:
7305 * Helper function to construct a list of #vik_trw_track_list_t
7307 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7309 GList *tracks_and_layers = NULL;
7310 // build tracks_and_layers list
7312 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7313 vtdl->trk = VIK_TRACK(tracks->data);
7315 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7316 tracks = g_list_next ( tracks );
7318 return tracks_and_layers;
7322 * trw_layer_create_track_list:
7324 * Create the latest list of tracks with the associated layer(s)
7325 * Although this will always be from a single layer here
7327 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7329 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7330 GList *tracks = NULL;
7331 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7332 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7334 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7336 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7339 static void trw_layer_tracks_stats ( menu_array_layer values )
7341 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7342 // There can only be one!
7343 if ( vtl->tracks_analysis_dialog )
7346 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7347 VIK_LAYER(vtl)->name,
7349 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7350 trw_layer_create_track_list,
7351 trw_layer_analyse_close );
7357 static void trw_layer_routes_stats ( menu_array_layer values )
7359 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7360 // There can only be one!
7361 if ( vtl->tracks_analysis_dialog )
7364 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7365 VIK_LAYER(vtl)->name,
7367 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7368 trw_layer_create_track_list,
7369 trw_layer_analyse_close );
7372 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7374 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7375 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7377 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7380 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7382 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7383 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7386 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7387 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7391 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7393 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7394 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7398 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7399 } else if ( !strncmp(wp->comment, "http", 4) ) {
7400 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7401 } else if ( !strncmp(wp->description, "http", 4) ) {
7402 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7406 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7408 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7410 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7412 // No actual change to the name supplied
7414 if (strcmp(newname, wp->name) == 0 )
7417 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7420 // An existing waypoint has been found with the requested name
7421 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7422 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7427 // Update WP name and refresh the treeview
7428 vik_waypoint_set_name (wp, newname);
7430 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7431 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7433 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7438 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7440 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7442 // No actual change to the name supplied
7444 if (strcmp(newname, trk->name) == 0)
7447 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7450 // An existing track has been found with the requested name
7451 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7452 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7456 // Update track name and refresh GUI parts
7457 vik_track_set_name (trk, newname);
7459 // Update any subwindows that could be displaying this track which has changed name
7460 // Only one Track Edit Window
7461 if ( l->current_tp_track == trk && l->tpwin ) {
7462 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7464 // Property Dialog of the track
7465 vik_trw_layer_propwin_update ( trk );
7467 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7468 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7470 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7475 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7477 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7479 // No actual change to the name supplied
7481 if (strcmp(newname, trk->name) == 0)
7484 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7487 // An existing track has been found with the requested name
7488 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7489 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7493 // Update track name and refresh GUI parts
7494 vik_track_set_name (trk, newname);
7496 // Update any subwindows that could be displaying this track which has changed name
7497 // Only one Track Edit Window
7498 if ( l->current_tp_track == trk && l->tpwin ) {
7499 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7501 // Property Dialog of the track
7502 vik_trw_layer_propwin_update ( trk );
7504 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7505 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7507 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7514 static gboolean is_valid_geocache_name ( gchar *str )
7516 gint len = strlen ( str );
7517 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]));
7520 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7522 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7523 a_acquire_set_filter_track ( trk );
7526 #ifdef VIK_CONFIG_GOOGLE
7527 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7529 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7530 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7533 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7535 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7537 gchar *escaped = uri_escape ( tr->comment );
7538 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7539 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7546 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7547 /* viewpoint is now available instead */
7548 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7550 static menu_array_sublayer pass_along;
7552 gboolean rv = FALSE;
7554 pass_along[MA_VTL] = l;
7555 pass_along[MA_VLP] = vlp;
7556 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7557 pass_along[MA_SUBLAYER_ID] = sublayer;
7558 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7559 pass_along[MA_VVP] = vvp;
7560 pass_along[MA_TV_ITER] = iter;
7561 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7563 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7567 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7568 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7569 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7570 gtk_widget_show ( item );
7572 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7573 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7574 if (tr && tr->property_dialog)
7575 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7577 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7578 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7579 if (tr && tr->property_dialog)
7580 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7583 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7584 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7585 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7586 gtk_widget_show ( item );
7588 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7591 gtk_widget_show ( item );
7593 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7595 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7596 gtk_widget_show ( item );
7598 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7600 // Always create separator as now there is always at least the transform menu option
7601 item = gtk_menu_item_new ();
7602 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7603 gtk_widget_show ( item );
7605 /* could be a right-click using the tool */
7606 if ( vlp != NULL ) {
7607 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7610 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7611 gtk_widget_show ( item );
7614 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7616 if ( wp && wp->name ) {
7617 if ( is_valid_geocache_name ( wp->name ) ) {
7618 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7621 gtk_widget_show ( item );
7623 #ifdef VIK_CONFIG_GEOTAG
7624 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7625 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7626 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7627 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7628 gtk_widget_show ( item );
7632 if ( wp && wp->image )
7634 // Set up image paramater
7635 pass_along[MA_MISC] = wp->image;
7637 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7638 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
7639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7640 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7641 gtk_widget_show ( item );
7643 #ifdef VIK_CONFIG_GEOTAG
7644 GtkWidget *geotag_submenu = gtk_menu_new ();
7645 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7646 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7647 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7648 gtk_widget_show ( item );
7649 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7651 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7652 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7653 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7654 gtk_widget_show ( item );
7656 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7657 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7658 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7659 gtk_widget_show ( item );
7666 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7667 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7668 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7669 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7670 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7671 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7672 gtk_widget_show ( item );
7678 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7679 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7680 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7681 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7682 gtk_widget_show ( item );
7683 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7684 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7685 gtk_widget_set_sensitive ( item, TRUE );
7687 gtk_widget_set_sensitive ( item, FALSE );
7690 item = gtk_menu_item_new ();
7691 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7692 gtk_widget_show ( item );
7695 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7698 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7699 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7700 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7701 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7702 gtk_widget_show ( item );
7705 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7707 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7708 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), 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 ( _("Goto _Waypoint...") );
7714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), 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 _All Waypoints") );
7720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7722 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7723 gtk_widget_show ( item );
7725 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7726 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7727 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7728 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7729 gtk_widget_show ( item );
7731 GtkWidget *vis_submenu = gtk_menu_new ();
7732 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7733 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7734 gtk_widget_show ( item );
7735 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7737 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7738 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7739 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), 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 ( _("_Hide All Waypoints") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), 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 ( _("_Toggle") );
7750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7752 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7753 gtk_widget_show ( item );
7755 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7756 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7761 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7765 if ( l->current_track && !l->current_track->is_route ) {
7766 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7767 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7768 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7769 gtk_widget_show ( item );
7771 item = gtk_menu_item_new ();
7772 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7773 gtk_widget_show ( item );
7776 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7777 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7779 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7780 gtk_widget_show ( item );
7782 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7783 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7784 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7785 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7786 gtk_widget_show ( item );
7787 // Make it available only when a new track *not* already in progress
7788 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7793 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7794 gtk_widget_show ( item );
7796 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7797 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7798 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7799 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7800 gtk_widget_show ( item );
7802 GtkWidget *vis_submenu = gtk_menu_new ();
7803 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7804 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7805 gtk_widget_show ( item );
7806 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7808 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), 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 ( _("_Hide All Tracks") );
7815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7817 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7818 gtk_widget_show ( item );
7820 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7823 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7825 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7826 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7828 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7829 gtk_widget_show ( item );
7831 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7832 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7833 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7834 gtk_widget_show ( item );
7837 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7841 if ( l->current_track && l->current_track->is_route ) {
7842 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7843 // Reuse finish track method
7844 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7845 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7846 gtk_widget_show ( item );
7848 item = gtk_menu_item_new ();
7849 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7850 gtk_widget_show ( item );
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7854 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7855 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7856 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7857 gtk_widget_show ( item );
7859 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7860 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7861 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7862 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7863 gtk_widget_show ( item );
7864 // Make it available only when a new track *not* already in progress
7865 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7867 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7868 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7870 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7871 gtk_widget_show ( item );
7873 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7874 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7875 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7876 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7877 gtk_widget_show ( item );
7879 GtkWidget *vis_submenu = gtk_menu_new ();
7880 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7881 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7882 gtk_widget_show ( item );
7883 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7885 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), 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 ( _("_Hide All Routes") );
7892 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7894 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7895 gtk_widget_show ( item );
7897 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7898 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7899 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7900 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7902 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7903 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7905 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7907 gtk_widget_show ( item );
7909 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7911 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7912 gtk_widget_show ( item );
7916 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7917 GtkWidget *submenu_sort = gtk_menu_new ();
7918 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7919 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7920 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7921 gtk_widget_show ( item );
7922 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7924 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7925 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7927 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7928 gtk_widget_show ( item );
7930 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7931 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7932 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7933 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7934 gtk_widget_show ( item );
7937 GtkWidget *upload_submenu = gtk_menu_new ();
7939 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7941 item = gtk_menu_item_new ();
7942 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7943 gtk_widget_show ( item );
7945 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7946 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7947 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7948 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7949 if ( l->current_track ) {
7950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7951 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7952 gtk_widget_show ( item );
7955 item = gtk_menu_item_new ();
7956 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7957 gtk_widget_show ( item );
7960 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7961 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7963 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7964 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7966 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7967 gtk_widget_show ( item );
7969 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7970 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7971 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7972 gtk_widget_show ( item );
7974 GtkWidget *goto_submenu;
7975 goto_submenu = gtk_menu_new ();
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7978 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7979 gtk_widget_show ( item );
7980 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7982 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7983 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), 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 ( _("\"_Center\"") );
7989 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), 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 ( _("_Endpoint") );
7995 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7996 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), 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 ( _("_Highest Altitude") );
8001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8003 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8004 gtk_widget_show ( item );
8006 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8007 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8009 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8010 gtk_widget_show ( item );
8012 // Routes don't have speeds
8013 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8016 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8017 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8018 gtk_widget_show ( item );
8021 GtkWidget *combine_submenu;
8022 combine_submenu = gtk_menu_new ();
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8024 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8025 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8026 gtk_widget_show ( item );
8027 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8029 // Routes don't have times or segments...
8030 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8031 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8033 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8034 gtk_widget_show ( item );
8036 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8038 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8039 gtk_widget_show ( item );
8042 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8044 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8045 gtk_widget_show ( item );
8047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8048 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8050 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8053 gtk_widget_show ( item );
8055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8056 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8058 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8060 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8061 gtk_widget_show ( item );
8063 GtkWidget *split_submenu;
8064 split_submenu = gtk_menu_new ();
8065 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8067 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8068 gtk_widget_show ( item );
8069 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8071 // Routes don't have times or segments...
8072 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8073 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8075 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8076 gtk_widget_show ( item );
8078 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8079 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8081 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8082 gtk_widget_show ( item );
8085 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8087 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8088 gtk_widget_show ( item );
8090 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8092 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8093 gtk_widget_show ( item );
8094 // Make it available only when a trackpoint is selected.
8095 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8097 GtkWidget *insert_submenu = gtk_menu_new ();
8098 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8100 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8101 gtk_widget_show ( item );
8102 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8104 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8106 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8107 gtk_widget_show ( item );
8108 // Make it available only when a point is selected
8109 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8111 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8113 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8114 gtk_widget_show ( item );
8115 // Make it available only when a point is selected
8116 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8118 GtkWidget *delete_submenu;
8119 delete_submenu = gtk_menu_new ();
8120 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8121 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8122 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8123 gtk_widget_show ( item );
8124 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8126 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8127 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8128 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8129 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8130 gtk_widget_show ( item );
8131 // Make it available only when a point is selected
8132 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8134 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8136 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8137 gtk_widget_show ( item );
8139 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8141 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8142 gtk_widget_show ( item );
8144 GtkWidget *transform_submenu;
8145 transform_submenu = gtk_menu_new ();
8146 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8147 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8148 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8149 gtk_widget_show ( item );
8150 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8152 GtkWidget *dem_submenu;
8153 dem_submenu = gtk_menu_new ();
8154 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8155 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
8156 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8157 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8159 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8161 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8162 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8163 gtk_widget_show ( item );
8165 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8167 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8168 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8169 gtk_widget_show ( item );
8171 GtkWidget *smooth_submenu;
8172 smooth_submenu = gtk_menu_new ();
8173 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8174 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8175 gtk_widget_show ( item );
8176 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8178 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8180 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8181 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8182 gtk_widget_show ( item );
8184 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8185 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8186 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8187 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8188 gtk_widget_show ( item );
8190 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8191 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8193 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8194 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8196 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8197 gtk_widget_show ( item );
8199 // Routes don't have timestamps - so this is only available for tracks
8200 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8201 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8203 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8204 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8205 gtk_widget_show ( item );
8208 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8209 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8211 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8212 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8213 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8214 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8215 gtk_widget_show ( item );
8217 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8218 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8219 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8220 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8221 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8222 gtk_widget_show ( item );
8225 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8227 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8228 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8230 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8231 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
8232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8233 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8234 gtk_widget_show ( item );
8237 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8238 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8240 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8243 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8244 gtk_widget_show ( item );
8246 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8247 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8249 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8250 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8251 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8252 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8253 gtk_widget_show ( item );
8255 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8256 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8257 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
8258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8259 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8260 gtk_widget_show ( item );
8263 // ATM can't upload a single waypoint but can do waypoints to a GPS
8264 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8265 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8266 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8267 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8268 gtk_widget_show ( item );
8269 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8271 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8272 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8273 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8274 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8275 gtk_widget_show ( item );
8279 // Only made available if a suitable program is installed
8280 if ( have_diary_program ) {
8281 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8282 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8283 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8285 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8286 gtk_widget_show ( item );
8290 #ifdef VIK_CONFIG_GOOGLE
8291 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8293 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8294 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8295 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8296 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8297 gtk_widget_show ( item );
8301 // Some things aren't usable with routes
8302 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8303 #ifdef VIK_CONFIG_OPENSTREETMAP
8304 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8305 // Convert internal pointer into track
8306 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8307 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8309 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8310 gtk_widget_show ( item );
8313 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8314 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8316 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8317 gtk_widget_show ( item );
8319 /* ATM This function is only available via the layers panel, due to needing a vlp */
8321 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8322 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8323 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8325 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8326 gtk_widget_show ( item );
8330 #ifdef VIK_CONFIG_GEOTAG
8331 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8332 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8333 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8334 gtk_widget_show ( item );
8338 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8339 // Only show on viewport popmenu when a trackpoint is selected
8340 if ( ! vlp && l->current_tpl ) {
8342 item = gtk_menu_item_new ();
8343 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8344 gtk_widget_show ( item );
8346 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8348 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8349 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8350 gtk_widget_show ( item );
8354 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8355 GtkWidget *transform_submenu;
8356 transform_submenu = gtk_menu_new ();
8357 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8358 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8359 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8360 gtk_widget_show ( item );
8361 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8363 GtkWidget *dem_submenu;
8364 dem_submenu = gtk_menu_new ();
8365 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8366 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
8367 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8368 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8370 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8372 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8373 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8374 gtk_widget_show ( item );
8376 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8378 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8379 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8380 gtk_widget_show ( item );
8383 gtk_widget_show_all ( GTK_WIDGET(menu) );
8388 // TODO: Probably better to rework this track manipulation in viktrack.c
8389 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8392 if (!vtl->current_tpl)
8395 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8396 VikTrackpoint *tp_other = NULL;
8399 if (!vtl->current_tpl->prev)
8401 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8403 if (!vtl->current_tpl->next)
8405 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8408 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8411 VikTrackpoint *tp_new = vik_trackpoint_new();
8412 struct LatLon ll_current, ll_other;
8413 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8414 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8416 /* main positional interpolation */
8417 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8418 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8420 /* Now other properties that can be interpolated */
8421 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8423 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8424 /* Note here the division is applied to each part, then added
8425 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8426 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8427 tp_new->has_timestamp = TRUE;
8430 if (tp_current->speed != NAN && tp_other->speed != NAN)
8431 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8433 /* TODO - improve interpolation of course, as it may not be correct.
8434 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8435 [similar applies if value is in radians] */
8436 if (tp_current->course != NAN && tp_other->course != NAN)
8437 tp_new->course = (tp_current->course + tp_other->course)/2;
8439 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8441 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8442 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8444 // Otherwise try routes
8445 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8449 gint index = g_list_index ( trk->trackpoints, tp_current );
8453 // NB no recalculation of bounds since it is inserted between points
8454 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8459 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8465 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8469 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8471 if ( vtl->current_tpl )
8473 vtl->current_tpl = NULL;
8474 vtl->current_tp_track = NULL;
8475 vtl->current_tp_id = NULL;
8476 vik_layer_emit_update(VIK_LAYER(vtl));
8480 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8482 g_assert ( vtl->tpwin != NULL );
8483 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8484 trw_layer_cancel_current_tp ( vtl, TRUE );
8486 if ( vtl->current_tpl == NULL )
8489 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8491 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8492 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8494 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8496 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8498 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8502 trw_layer_trackpoint_selected_delete ( vtl, tr );
8504 if ( vtl->current_tpl )
8505 // Reset dialog with the available adjacent trackpoint
8506 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8508 vik_layer_emit_update(VIK_LAYER(vtl));
8510 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8512 if ( vtl->current_tp_track )
8513 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8514 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8516 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8518 if ( vtl->current_tp_track )
8519 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8520 vik_layer_emit_update(VIK_LAYER(vtl));
8522 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8524 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8525 vik_layer_emit_update(VIK_LAYER(vtl));
8527 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8528 vik_layer_emit_update(VIK_LAYER(vtl));
8532 * trw_layer_dialog_shift:
8533 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8535 * Try to reposition a dialog if it's over the specified coord
8536 * so to not obscure the item of interest
8538 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8540 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8542 // Attempt force dialog to be shown so we can find out where it is more reliably...
8543 while ( gtk_events_pending() )
8544 gtk_main_iteration ();
8546 // get parent window position & size
8547 gint win_pos_x, win_pos_y;
8548 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8550 gint win_size_x, win_size_y;
8551 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8553 // get own dialog size
8554 gint dia_size_x, dia_size_y;
8555 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8557 // get own dialog position
8558 gint dia_pos_x, dia_pos_y;
8559 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8561 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8562 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8564 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8566 gint vp_xx, vp_yy; // In viewport pixels
8567 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8569 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8573 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8575 // Transform Viewport pixels into absolute pixels
8576 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8577 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8579 // Is dialog over the point (to within an ^^ edge value)
8580 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8581 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8585 gint hh = vik_viewport_get_height ( vvp );
8587 // Consider the difference in viewport to the full window
8588 gint offset_y = dest_y;
8589 // Add difference between dialog and window sizes
8590 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8592 if ( vp_yy > hh/2 ) {
8593 // Point in bottom half, move window to top half
8594 gtk_window_move ( dialog, dia_pos_x, offset_y );
8597 // Point in top half, move dialog down
8598 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8602 // Shift left<->right
8603 gint ww = vik_viewport_get_width ( vvp );
8605 // Consider the difference in viewport to the full window
8606 gint offset_x = dest_x;
8607 // Add difference between dialog and window sizes
8608 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8610 if ( vp_xx > ww/2 ) {
8611 // Point on right, move window to left
8612 gtk_window_move ( dialog, offset_x, dia_pos_y );
8615 // Point on left, move right
8616 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8624 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8628 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8629 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8630 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8631 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8633 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8635 if ( vtl->current_tpl ) {
8636 // get tp pixel position
8637 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8639 // Shift up<->down to try not to obscure the trackpoint.
8640 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8644 if ( vtl->current_tpl )
8645 if ( vtl->current_tp_track )
8646 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8647 /* set layer name and TP data */
8650 /***************************************************************************
8652 ***************************************************************************/
8654 /*** Utility data structures and functions ****/
8658 gint closest_x, closest_y;
8659 gboolean draw_images;
8660 gpointer *closest_wp_id;
8661 VikWaypoint *closest_wp;
8667 gint closest_x, closest_y;
8668 gpointer closest_track_id;
8669 VikTrackpoint *closest_tp;
8675 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8681 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8683 // If waypoint has an image then use the image size to select
8684 if ( params->draw_images && wp->image ) {
8685 gint slackx, slacky;
8686 slackx = wp->image_width / 2;
8687 slacky = wp->image_height / 2;
8689 if ( x <= params->x + slackx && x >= params->x - slackx
8690 && y <= params->y + slacky && y >= params->y - slacky ) {
8691 params->closest_wp_id = id;
8692 params->closest_wp = wp;
8693 params->closest_x = x;
8694 params->closest_y = y;
8697 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8698 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8699 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8701 params->closest_wp_id = id;
8702 params->closest_wp = wp;
8703 params->closest_x = x;
8704 params->closest_y = y;
8708 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8710 GList *tpl = t->trackpoints;
8716 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8722 tp = VIK_TRACKPOINT(tpl->data);
8724 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8726 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8727 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8728 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8730 params->closest_track_id = id;
8731 params->closest_tp = tp;
8732 params->closest_tpl = tpl;
8733 params->closest_x = x;
8734 params->closest_y = y;
8740 // ATM: Leave this as 'Track' only.
8741 // Not overly bothered about having a snap to route trackpoint capability
8742 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8744 TPSearchParams params;
8748 params.closest_track_id = NULL;
8749 params.closest_tp = NULL;
8750 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8751 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8752 return params.closest_tp;
8755 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8757 WPSearchParams params;
8761 params.draw_images = vtl->drawimages;
8762 params.closest_wp = NULL;
8763 params.closest_wp_id = NULL;
8764 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8765 return params.closest_wp;
8769 // Some forward declarations
8770 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8771 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8772 static void marker_end_move ( tool_ed_t *t );
8775 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8779 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8781 // Here always allow snapping back to the original location
8782 // this is useful when one decides not to move the thing afterall
8783 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8786 if ( event->state & GDK_CONTROL_MASK )
8788 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8790 new_coord = tp->coord;
8794 if ( event->state & GDK_SHIFT_MASK )
8796 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8798 new_coord = wp->coord;
8802 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8804 marker_moveto ( t, x, y );
8811 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8813 if ( t->holding && event->button == 1 )
8816 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8819 if ( event->state & GDK_CONTROL_MASK )
8821 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8823 new_coord = tp->coord;
8827 if ( event->state & GDK_SHIFT_MASK )
8829 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8831 new_coord = wp->coord;
8834 marker_end_move ( t );
8836 // Determine if working on a waypoint or a trackpoint
8837 if ( t->is_waypoint ) {
8838 // Update waypoint position
8839 vtl->current_wp->coord = new_coord;
8840 trw_layer_calculate_bounds_waypoints ( vtl );
8841 // Reset waypoint pointer
8842 vtl->current_wp = NULL;
8843 vtl->current_wp_id = NULL;
8846 if ( vtl->current_tpl ) {
8847 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8849 if ( vtl->current_tp_track )
8850 vik_track_calculate_bounds ( vtl->current_tp_track );
8853 if ( vtl->current_tp_track )
8854 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8855 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8859 vik_layer_emit_update ( VIK_LAYER(vtl) );
8866 Returns true if a waypoint or track is found near the requested event position for this particular layer
8867 The item found is automatically selected
8868 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8870 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8872 if ( event->button != 1 )
8875 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8878 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8882 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8884 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8886 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8887 WPSearchParams wp_params;
8888 wp_params.vvp = vvp;
8889 wp_params.x = event->x;
8890 wp_params.y = event->y;
8891 wp_params.draw_images = vtl->drawimages;
8892 wp_params.closest_wp_id = NULL;
8893 wp_params.closest_wp = NULL;
8895 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8897 if ( wp_params.closest_wp ) {
8900 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8902 // Too easy to move it so must be holding shift to start immediately moving it
8903 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8904 if ( event->state & GDK_SHIFT_MASK ||
8905 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8906 // Put into 'move buffer'
8907 // NB vvp & vw already set in tet
8908 tet->vtl = (gpointer)vtl;
8909 tet->is_waypoint = TRUE;
8911 marker_begin_move (tet, event->x, event->y);
8914 vtl->current_wp = wp_params.closest_wp;
8915 vtl->current_wp_id = wp_params.closest_wp_id;
8917 if ( event->type == GDK_2BUTTON_PRESS ) {
8918 if ( vtl->current_wp->image ) {
8919 menu_array_sublayer values;
8920 values[MA_VTL] = vtl;
8921 values[MA_MISC] = vtl->current_wp->image;
8922 trw_layer_show_picture ( values );
8926 vik_layer_emit_update ( VIK_LAYER(vtl) );
8932 // Used for both track and route lists
8933 TPSearchParams tp_params;
8934 tp_params.vvp = vvp;
8935 tp_params.x = event->x;
8936 tp_params.y = event->y;
8937 tp_params.closest_track_id = NULL;
8938 tp_params.closest_tp = NULL;
8939 tp_params.closest_tpl = NULL;
8940 tp_params.bbox = bbox;
8942 if (vtl->tracks_visible) {
8943 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8945 if ( tp_params.closest_tp ) {
8947 // Always select + highlight the track
8948 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8950 tet->is_waypoint = FALSE;
8952 // Select the Trackpoint
8953 // Can move it immediately when control held or it's the previously selected tp
8954 if ( event->state & GDK_CONTROL_MASK ||
8955 vtl->current_tpl == tp_params.closest_tpl ) {
8956 // Put into 'move buffer'
8957 // NB vvp & vw already set in tet
8958 tet->vtl = (gpointer)vtl;
8959 marker_begin_move (tet, event->x, event->y);
8962 vtl->current_tpl = tp_params.closest_tpl;
8963 vtl->current_tp_id = tp_params.closest_track_id;
8964 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8966 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8969 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8971 vik_layer_emit_update ( VIK_LAYER(vtl) );
8976 // Try again for routes
8977 if (vtl->routes_visible) {
8978 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8980 if ( tp_params.closest_tp ) {
8982 // Always select + highlight the track
8983 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8985 tet->is_waypoint = FALSE;
8987 // Select the Trackpoint
8988 // Can move it immediately when control held or it's the previously selected tp
8989 if ( event->state & GDK_CONTROL_MASK ||
8990 vtl->current_tpl == tp_params.closest_tpl ) {
8991 // Put into 'move buffer'
8992 // NB vvp & vw already set in tet
8993 tet->vtl = (gpointer)vtl;
8994 marker_begin_move (tet, event->x, event->y);
8997 vtl->current_tpl = tp_params.closest_tpl;
8998 vtl->current_tp_id = tp_params.closest_track_id;
8999 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9001 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9004 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9006 vik_layer_emit_update ( VIK_LAYER(vtl) );
9011 /* these aren't the droids you're looking for */
9012 vtl->current_wp = NULL;
9013 vtl->current_wp_id = NULL;
9014 trw_layer_cancel_current_tp ( vtl, FALSE );
9017 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9022 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9024 if ( event->button != 3 )
9027 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9030 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9033 /* Post menu for the currently selected item */
9035 /* See if a track is selected */
9036 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9037 if ( track && track->visible ) {
9039 if ( track->name ) {
9041 if ( vtl->track_right_click_menu )
9042 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9044 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9051 if ( track->is_route )
9052 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9054 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9056 if ( trkf && udataU.uuid ) {
9059 if ( track->is_route )
9060 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9062 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9064 trw_layer_sublayer_add_menu_items ( vtl,
9065 vtl->track_right_click_menu,
9067 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9073 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9079 /* See if a waypoint is selected */
9080 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9081 if ( waypoint && waypoint->visible ) {
9082 if ( waypoint->name ) {
9084 if ( vtl->wp_right_click_menu )
9085 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9087 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9090 udata.wp = waypoint;
9093 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9095 if ( wpf && udata.uuid ) {
9096 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9098 trw_layer_sublayer_add_menu_items ( vtl,
9099 vtl->wp_right_click_menu,
9101 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9106 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9115 /* background drawing hook, to be passed the viewport */
9116 static gboolean tool_sync_done = TRUE;
9118 static gboolean tool_sync(gpointer data)
9120 VikViewport *vvp = data;
9121 gdk_threads_enter();
9122 vik_viewport_sync(vvp);
9123 tool_sync_done = TRUE;
9124 gdk_threads_leave();
9128 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9131 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9132 gdk_gc_set_function ( t->gc, GDK_INVERT );
9133 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9134 vik_viewport_sync(t->vvp);
9139 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9141 VikViewport *vvp = t->vvp;
9142 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9143 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9147 if (tool_sync_done) {
9148 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9149 tool_sync_done = FALSE;
9153 static void marker_end_move ( tool_ed_t *t )
9155 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9156 g_object_unref ( t->gc );
9160 /*** Edit waypoint ****/
9162 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9164 tool_ed_t *t = g_new(tool_ed_t, 1);
9170 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9175 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9177 WPSearchParams params;
9178 tool_ed_t *t = data;
9179 VikViewport *vvp = t->vvp;
9181 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9188 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9191 if ( vtl->current_wp && vtl->current_wp->visible )
9193 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9195 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9197 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9198 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9200 if ( event->button == 3 )
9201 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9203 marker_begin_move(t, event->x, event->y);
9210 params.x = event->x;
9211 params.y = event->y;
9212 params.draw_images = vtl->drawimages;
9213 params.closest_wp_id = NULL;
9214 params.closest_wp = NULL;
9215 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9216 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9218 if ( event->button == 3 )
9219 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9221 marker_begin_move(t, event->x, event->y);
9224 else if ( params.closest_wp )
9226 if ( event->button == 3 )
9227 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9229 vtl->waypoint_rightclick = FALSE;
9231 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9233 vtl->current_wp = params.closest_wp;
9234 vtl->current_wp_id = params.closest_wp_id;
9236 /* could make it so don't update if old WP is off screen and new is null but oh well */
9237 vik_layer_emit_update ( VIK_LAYER(vtl) );
9241 vtl->current_wp = NULL;
9242 vtl->current_wp_id = NULL;
9243 vtl->waypoint_rightclick = FALSE;
9244 vik_layer_emit_update ( VIK_LAYER(vtl) );
9248 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9250 tool_ed_t *t = data;
9251 VikViewport *vvp = t->vvp;
9253 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9258 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9261 if ( event->state & GDK_CONTROL_MASK )
9263 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9265 new_coord = tp->coord;
9269 if ( event->state & GDK_SHIFT_MASK )
9271 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9272 if ( wp && wp != vtl->current_wp )
9273 new_coord = wp->coord;
9278 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9280 marker_moveto ( t, x, y );
9287 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9289 tool_ed_t *t = data;
9290 VikViewport *vvp = t->vvp;
9292 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9295 if ( t->holding && event->button == 1 )
9298 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9301 if ( event->state & GDK_CONTROL_MASK )
9303 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9305 new_coord = tp->coord;
9309 if ( event->state & GDK_SHIFT_MASK )
9311 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9312 if ( wp && wp != vtl->current_wp )
9313 new_coord = wp->coord;
9316 marker_end_move ( t );
9318 vtl->current_wp->coord = new_coord;
9320 trw_layer_calculate_bounds_waypoints ( vtl );
9321 vik_layer_emit_update ( VIK_LAYER(vtl) );
9324 /* PUT IN RIGHT PLACE!!! */
9325 if ( event->button == 3 && vtl->waypoint_rightclick )
9327 if ( vtl->wp_right_click_menu )
9328 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9329 if ( vtl->current_wp ) {
9330 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9331 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 );
9332 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9334 vtl->waypoint_rightclick = FALSE;
9339 /*** New track ****/
9341 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9348 GdkDrawable *drawable;
9354 * Draw specified pixmap
9356 static gboolean draw_sync ( gpointer data )
9358 draw_sync_t *ds = (draw_sync_t*) data;
9359 // Sometimes don't want to draw
9360 // normally because another update has taken precedent such as panning the display
9361 // which means this pixmap is no longer valid
9362 if ( ds->vtl->draw_sync_do ) {
9363 gdk_threads_enter();
9364 gdk_draw_drawable (ds->drawable,
9367 0, 0, 0, 0, -1, -1);
9368 ds->vtl->draw_sync_done = TRUE;
9369 gdk_threads_leave();
9375 static gchar* distance_string (gdouble distance)
9379 /* draw label with distance */
9380 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9381 switch (dist_units) {
9382 case VIK_UNITS_DISTANCE_MILES:
9383 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9384 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9385 } else if (distance < 1609.4) {
9386 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9388 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9392 // VIK_UNITS_DISTANCE_KILOMETRES
9393 if (distance >= 1000 && distance < 100000) {
9394 g_sprintf(str, "%3.2f km", distance/1000.0);
9395 } else if (distance < 1000) {
9396 g_sprintf(str, "%d m", (int)distance);
9398 g_sprintf(str, "%d km", (int)distance/1000);
9402 return g_strdup (str);
9406 * Actually set the message in statusbar
9408 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9410 // Only show elevation data when track has some elevation properties
9411 gchar str_gain_loss[64];
9412 str_gain_loss[0] = '\0';
9413 gchar str_last_step[64];
9414 str_last_step[0] = '\0';
9415 gchar *str_total = distance_string (distance);
9417 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9418 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9419 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9421 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9424 if ( last_step > 0 ) {
9425 gchar *tmp = distance_string (last_step);
9426 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9430 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9432 // Write with full gain/loss information
9433 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9434 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9436 g_free ( str_total );
9440 * Figure out what information should be set in the statusbar and then write it
9442 static void update_statusbar ( VikTrwLayer *vtl )
9444 // Get elevation data
9445 gdouble elev_gain, elev_loss;
9446 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9448 /* Find out actual distance of current track */
9449 gdouble distance = vik_track_get_length (vtl->current_track);
9451 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9455 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9457 /* if we haven't sync'ed yet, we don't have time to do more. */
9458 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9459 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9461 static GdkPixmap *pixmap = NULL;
9463 // Need to check in case window has been resized
9464 w1 = vik_viewport_get_width(vvp);
9465 h1 = vik_viewport_get_height(vvp);
9467 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9469 gdk_drawable_get_size (pixmap, &w2, &h2);
9470 if (w1 != w2 || h1 != h2) {
9471 g_object_unref ( G_OBJECT ( pixmap ) );
9472 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9475 // Reset to background
9476 gdk_draw_drawable (pixmap,
9477 vtl->current_track_newpoint_gc,
9478 vik_viewport_get_pixmap(vvp),
9479 0, 0, 0, 0, -1, -1);
9481 draw_sync_t *passalong;
9484 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9486 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9487 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9488 // thus when we come to reset to the background it would include what we have already drawn!!
9489 gdk_draw_line ( pixmap,
9490 vtl->current_track_newpoint_gc,
9491 x1, y1, event->x, event->y );
9492 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9494 /* Find out actual distance of current track */
9495 gdouble distance = vik_track_get_length (vtl->current_track);
9497 // Now add distance to where the pointer is //
9500 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9501 vik_coord_to_latlon ( &coord, &ll );
9502 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9503 distance = distance + last_step;
9505 // Get elevation data
9506 gdouble elev_gain, elev_loss;
9507 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9509 // Adjust elevation data (if available) for the current pointer position
9511 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9512 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9513 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9514 // Adjust elevation of last track point
9515 if ( elev_new > last_tpt->altitude )
9517 elev_gain += elev_new - last_tpt->altitude;
9520 elev_loss += last_tpt->altitude - elev_new;
9525 // Display of the distance 'tooltip' during track creation is controlled by a preference
9527 if ( a_vik_get_create_track_tooltip() ) {
9529 gchar *str = distance_string (distance);
9531 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9532 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9533 pango_layout_set_text (pl, str, -1);
9535 pango_layout_get_pixel_size ( pl, &wd, &hd );
9538 // offset from cursor a bit depending on font size
9542 // Create a background block to make the text easier to read over the background map
9543 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9544 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9545 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9547 g_object_unref ( G_OBJECT ( pl ) );
9548 g_object_unref ( G_OBJECT ( background_block_gc ) );
9552 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9553 passalong->vtl = vtl;
9554 passalong->pixmap = pixmap;
9555 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9556 passalong->gc = vtl->current_track_newpoint_gc;
9560 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9562 // Update statusbar with full gain/loss information
9563 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9565 // draw pixmap when we have time to
9566 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9567 vtl->draw_sync_done = FALSE;
9568 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9570 return VIK_LAYER_TOOL_ACK;
9573 // NB vtl->current_track must be valid
9574 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9577 if ( vtl->current_track->trackpoints ) {
9578 // TODO rework this...
9579 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9580 GList *last = g_list_last(vtl->current_track->trackpoints);
9581 g_free ( last->data );
9582 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9584 vik_track_calculate_bounds ( vtl->current_track );
9588 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9590 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9591 vtl->current_track = NULL;
9592 vik_layer_emit_update ( VIK_LAYER(vtl) );
9594 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9595 undo_trackpoint_add ( vtl );
9596 update_statusbar ( vtl );
9597 vik_layer_emit_update ( VIK_LAYER(vtl) );
9604 * Common function to handle trackpoint button requests on either a route or a track
9605 * . enables adding a point via normal click
9606 * . enables removal of last point via right click
9607 * . finishing of the track or route via double clicking
9609 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9613 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9616 if ( event->button == 2 ) {
9617 // As the display is panning, the new track pixmap is now invalid so don't draw it
9618 // otherwise this drawing done results in flickering back to an old image
9619 vtl->draw_sync_do = FALSE;
9623 if ( event->button == 3 )
9625 if ( !vtl->current_track )
9627 undo_trackpoint_add ( vtl );
9628 update_statusbar ( vtl );
9629 vik_layer_emit_update ( VIK_LAYER(vtl) );
9633 if ( event->type == GDK_2BUTTON_PRESS )
9635 /* subtract last (duplicate from double click) tp then end */
9636 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9638 /* undo last, then end */
9639 undo_trackpoint_add ( vtl );
9640 vtl->current_track = NULL;
9642 vik_layer_emit_update ( VIK_LAYER(vtl) );
9646 tp = vik_trackpoint_new();
9647 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9649 /* snap to other TP */
9650 if ( event->state & GDK_CONTROL_MASK )
9652 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9654 tp->coord = other_tp->coord;
9657 tp->newsegment = FALSE;
9658 tp->has_timestamp = FALSE;
9661 if ( vtl->current_track ) {
9662 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9663 /* Auto attempt to get elevation from DEM data (if it's available) */
9664 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9667 vtl->ct_x1 = vtl->ct_x2;
9668 vtl->ct_y1 = vtl->ct_y2;
9669 vtl->ct_x2 = event->x;
9670 vtl->ct_y2 = event->y;
9672 vik_layer_emit_update ( VIK_LAYER(vtl) );
9676 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9678 // if we were running the route finder, cancel it
9679 vtl->route_finder_started = FALSE;
9681 // ----------------------------------------------------- if current is a route - switch to new track
9682 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9684 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9685 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9687 new_track_create_common ( vtl, name );
9693 return tool_new_track_or_route_click ( vtl, event, vvp );
9696 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9698 if ( event->button == 2 ) {
9699 // Pan moving ended - enable potential point drawing again
9700 vtl->draw_sync_do = TRUE;
9701 vtl->draw_sync_done = TRUE;
9705 /*** New route ****/
9707 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9712 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9714 // if we were running the route finder, cancel it
9715 vtl->route_finder_started = FALSE;
9717 // -------------------------- if current is a track - switch to new route,
9718 if ( event->button == 1 && ( ! vtl->current_track ||
9719 (vtl->current_track && !vtl->current_track->is_route ) ) )
9721 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9722 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9723 new_route_create_common ( vtl, name );
9729 return tool_new_track_or_route_click ( vtl, event, vvp );
9732 /*** New waypoint ****/
9734 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9739 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9742 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9744 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9745 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9746 trw_layer_calculate_bounds_waypoints ( vtl );
9747 vik_layer_emit_update ( VIK_LAYER(vtl) );
9753 /*** Edit trackpoint ****/
9755 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9757 tool_ed_t *t = g_new(tool_ed_t, 1);
9763 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9768 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9770 tool_ed_t *t = data;
9771 VikViewport *vvp = t->vvp;
9772 TPSearchParams params;
9773 /* OUTDATED DOCUMENTATION:
9774 find 5 pixel range on each side. then put these UTM, and a pointer
9775 to the winning track name (and maybe the winning track itself), and a
9776 pointer to the winning trackpoint, inside an array or struct. pass
9777 this along, do a foreach on the tracks which will do a foreach on the
9780 params.x = event->x;
9781 params.y = event->y;
9782 params.closest_track_id = NULL;
9783 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9784 params.closest_tp = NULL;
9785 params.closest_tpl = NULL;
9786 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9788 if ( event->button != 1 )
9791 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9794 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9797 if ( vtl->current_tpl )
9799 /* 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.) */
9800 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9801 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9806 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9808 if ( current_tr->visible &&
9809 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9810 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9811 marker_begin_move ( t, event->x, event->y );
9817 if ( vtl->tracks_visible )
9818 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9820 if ( params.closest_tp )
9822 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9823 vtl->current_tpl = params.closest_tpl;
9824 vtl->current_tp_id = params.closest_track_id;
9825 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9826 trw_layer_tpwin_init ( vtl );
9827 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9828 vik_layer_emit_update ( VIK_LAYER(vtl) );
9832 if ( vtl->routes_visible )
9833 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9835 if ( params.closest_tp )
9837 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9838 vtl->current_tpl = params.closest_tpl;
9839 vtl->current_tp_id = params.closest_track_id;
9840 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9841 trw_layer_tpwin_init ( vtl );
9842 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9843 vik_layer_emit_update ( VIK_LAYER(vtl) );
9847 /* these aren't the droids you're looking for */
9851 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9853 tool_ed_t *t = data;
9854 VikViewport *vvp = t->vvp;
9856 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9862 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9865 if ( event->state & GDK_CONTROL_MASK )
9867 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9868 if ( tp && tp != vtl->current_tpl->data )
9869 new_coord = tp->coord;
9871 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9874 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9875 marker_moveto ( t, x, y );
9883 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9885 tool_ed_t *t = data;
9886 VikViewport *vvp = t->vvp;
9888 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9890 if ( event->button != 1)
9895 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9898 if ( event->state & GDK_CONTROL_MASK )
9900 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9901 if ( tp && tp != vtl->current_tpl->data )
9902 new_coord = tp->coord;
9905 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9906 if ( vtl->current_tp_track )
9907 vik_track_calculate_bounds ( vtl->current_tp_track );
9909 marker_end_move ( t );
9911 /* diff dist is diff from orig */
9913 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9915 vik_layer_emit_update ( VIK_LAYER(vtl) );
9922 /*** Extended Route Finder ***/
9924 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9929 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9932 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9935 vik_layer_emit_update ( VIK_LAYER(vtl) );
9937 /* remove last ' to:...' */
9938 if ( vtl->current_track->comment ) {
9939 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9940 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9941 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9942 last_to - vtl->current_track->comment - 1);
9943 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9950 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9953 if ( !vtl ) return FALSE;
9954 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9955 if ( event->button == 3 && vtl->current_track ) {
9956 tool_extended_route_finder_undo ( vtl );
9958 else if ( event->button == 2 ) {
9959 vtl->draw_sync_do = FALSE;
9962 // if we started the track but via undo deleted all the track points, begin again
9963 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
9964 return tool_new_track_or_route_click ( vtl, event, vvp );
9966 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
9967 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
9968 struct LatLon start, end;
9970 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
9971 vik_coord_to_latlon ( &(tp_start->coord), &start );
9972 vik_coord_to_latlon ( &(tmp), &end );
9974 vtl->route_finder_started = TRUE;
9975 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9977 // update UI to let user know what's going on
9978 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9979 VikRoutingEngine *engine = vik_routing_default_engine ( );
9981 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
9984 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
9985 vik_routing_engine_get_label ( engine ),
9986 start.lat, start.lon, end.lat, end.lon );
9987 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
9989 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
9992 /* Give GTK a change to display the new status bar before querying the web */
9993 while ( gtk_events_pending ( ) )
9994 gtk_main_iteration ( );
9996 gboolean find_status = vik_routing_default_find ( vtl, start, end );
9998 /* Update UI to say we're done */
9999 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10000 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10001 vik_routing_engine_get_label ( engine ),
10002 start.lat, start.lon, end.lat, end.lon )
10003 : g_strdup_printf ( _("Error getting route from %s."),
10004 vik_routing_engine_get_label ( engine ) );
10005 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10008 vik_layer_emit_update ( VIK_LAYER(vtl) );
10010 vtl->current_track = NULL;
10012 // create a new route where we will add the planned route to
10013 gboolean ret = tool_new_route_click( vtl, event, vvp );
10015 vtl->route_finder_started = TRUE;
10022 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10024 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10025 vtl->route_finder_started = FALSE;
10026 vtl->current_track = NULL;
10027 vik_layer_emit_update ( VIK_LAYER(vtl) );
10029 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10030 tool_extended_route_finder_undo ( vtl );
10037 /*** Show picture ****/
10039 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10044 /* Params are: vvp, event, last match found or NULL */
10045 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10047 if ( wp->image && wp->visible )
10049 gint x, y, slackx, slacky;
10050 GdkEventButton *event = (GdkEventButton *) params[1];
10052 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10053 slackx = wp->image_width / 2;
10054 slacky = wp->image_height / 2;
10055 if ( x <= event->x + slackx && x >= event->x - slackx
10056 && y <= event->y + slacky && y >= event->y - slacky )
10058 params[2] = wp->image; /* we've found a match. however continue searching
10059 * since we want to find the last match -- that
10060 * is, the match that was drawn last. */
10065 static void trw_layer_show_picture ( menu_array_sublayer values )
10067 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10069 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10070 #else /* WINDOWS */
10071 GError *err = NULL;
10072 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10073 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10074 g_free ( quoted_file );
10075 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10077 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() );
10078 g_error_free ( err );
10081 #endif /* WINDOWS */
10084 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10086 gpointer params[3] = { vvp, event, NULL };
10087 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10089 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10092 static menu_array_sublayer values;
10093 values[MA_VTL] = vtl;
10094 values[MA_MISC] = params[2];
10095 trw_layer_show_picture ( values );
10096 return TRUE; /* found a match */
10099 return FALSE; /* go through other layers, searching for a match */
10102 /***************************************************************************
10104 ***************************************************************************/
10107 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10109 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10110 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10113 /* Structure for thumbnail creating data used in the background thread */
10115 VikTrwLayer *vtl; // Layer needed for redrawing
10116 GSList *pics; // Image list
10117 } thumbnail_create_thread_data;
10119 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10121 guint total = g_slist_length(tctd->pics), done = 0;
10122 while ( tctd->pics )
10124 a_thumbnails_create ( (gchar *) tctd->pics->data );
10125 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10127 return -1; /* Abort thread */
10129 tctd->pics = tctd->pics->next;
10132 // Redraw to show the thumbnails as they are now created
10133 if ( IS_VIK_LAYER(tctd->vtl) )
10134 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10139 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10141 while ( tctd->pics )
10143 g_free ( tctd->pics->data );
10144 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10149 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10151 if ( ! vtl->has_verified_thumbnails )
10153 GSList *pics = NULL;
10154 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10157 gint len = g_slist_length ( pics );
10158 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10159 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10162 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10164 (vik_thr_func) create_thumbnails_thread,
10166 (vik_thr_free_func) thumbnail_create_thread_free,
10174 static const gchar* my_track_colors ( gint ii )
10176 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10188 // Fast and reliable way of returning a colour
10189 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10192 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10194 GHashTableIter iter;
10195 gpointer key, value;
10199 g_hash_table_iter_init ( &iter, vtl->tracks );
10201 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10203 // Tracks get a random spread of colours if not already assigned
10204 if ( ! VIK_TRACK(value)->has_color ) {
10205 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10206 VIK_TRACK(value)->color = vtl->track_color;
10208 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10210 VIK_TRACK(value)->has_color = TRUE;
10213 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10216 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10222 g_hash_table_iter_init ( &iter, vtl->routes );
10224 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10226 // Routes get an intermix of reds
10227 if ( ! VIK_TRACK(value)->has_color ) {
10229 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10231 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10232 VIK_TRACK(value)->has_color = TRUE;
10235 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10242 * (Re)Calculate the bounds of the waypoints in this layer,
10243 * This should be called whenever waypoints are changed
10245 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10247 struct LatLon topleft = { 0.0, 0.0 };
10248 struct LatLon bottomright = { 0.0, 0.0 };
10251 GHashTableIter iter;
10252 gpointer key, value;
10254 g_hash_table_iter_init ( &iter, vtl->waypoints );
10256 // Set bounds to first point
10257 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10258 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10259 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10262 // Ensure there is another point...
10263 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10265 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10267 // See if this point increases the bounds.
10268 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10270 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10271 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10272 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10273 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10277 vtl->waypoints_bbox.north = topleft.lat;
10278 vtl->waypoints_bbox.east = bottomright.lon;
10279 vtl->waypoints_bbox.south = bottomright.lat;
10280 vtl->waypoints_bbox.west = topleft.lon;
10283 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10285 vik_track_calculate_bounds ( trk );
10288 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10290 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10291 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10294 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10296 if ( ! VIK_LAYER(vtl)->vt )
10299 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10300 if ( g_hash_table_size (vtl->tracks) > 1 )
10301 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10303 if ( g_hash_table_size (vtl->routes) > 1 )
10304 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10306 if ( g_hash_table_size (vtl->waypoints) > 1 )
10307 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10310 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10312 if ( VIK_LAYER(vtl)->realized )
10313 trw_layer_verify_thumbnails ( vtl, vvp );
10314 trw_layer_track_alloc_colors ( vtl );
10316 trw_layer_calculate_bounds_waypoints ( vtl );
10317 trw_layer_calculate_bounds_tracks ( vtl );
10319 // Apply treeview sort after loading all the tracks for this layer
10320 // (rather than sorted insert on each individual track additional)
10321 // and after subsequent changes to the properties as the specified order may have changed.
10322 // since the sorting of a treeview section is now very quick
10323 // NB sorting is also performed after every name change as well to maintain the list order
10324 trw_layer_sort_all ( vtl );
10326 // Setting metadata time if not otherwise set
10327 if ( vtl->metadata ) {
10329 gboolean need_to_set_time = TRUE;
10330 if ( vtl->metadata->timestamp ) {
10331 need_to_set_time = FALSE;
10332 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10333 need_to_set_time = TRUE;
10336 if ( need_to_set_time ) {
10337 // Could rewrite this as a general get first time of a TRW Layer function
10338 GTimeVal timestamp;
10339 timestamp.tv_usec = 0;
10340 gboolean has_timestamp = FALSE;
10343 gl = g_hash_table_get_values ( vtl->tracks );
10344 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10345 gl = g_list_first ( gl );
10347 // Check times of tracks
10349 // Only need to check the first track as they have been sorted by time
10350 VikTrack *trk = (VikTrack*)gl->data;
10351 // Assume trackpoints already sorted by time
10352 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10353 if ( tpt && tpt->has_timestamp ) {
10354 timestamp.tv_sec = tpt->timestamp;
10355 has_timestamp = TRUE;
10357 g_list_free ( gl );
10360 if ( !has_timestamp ) {
10361 // 'Last' resort - current time
10362 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10363 g_get_current_time ( ×tamp );
10365 // Check times of waypoints
10366 gl = g_hash_table_get_values ( vtl->waypoints );
10368 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10369 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10370 if ( wpt->has_timestamp ) {
10371 if ( timestamp.tv_sec > wpt->timestamp ) {
10372 timestamp.tv_sec = wpt->timestamp;
10373 has_timestamp = TRUE;
10377 g_list_free ( gl );
10380 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10385 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10387 return vtl->coord_mode;
10391 * Uniquify the whole layer
10392 * Also requires the layers panel as the names shown there need updating too
10393 * Returns whether the operation was successful or not
10395 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10397 if ( vtl && vlp ) {
10398 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10399 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10400 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10406 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10408 vik_coord_convert ( &(wp->coord), *dest_mode );
10411 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10413 vik_track_convert ( tr, *dest_mode );
10416 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10418 if ( vtl->coord_mode != dest_mode )
10420 vtl->coord_mode = dest_mode;
10421 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10422 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10423 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10427 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10429 vtl->menu_selection = selection;
10432 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10434 return (vtl->menu_selection);
10437 /* ----------- Downloading maps along tracks --------------- */
10439 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10441 /* TODO: calculating based on current size of viewport */
10442 const gdouble w_at_zoom_0_125 = 0.0013;
10443 const gdouble h_at_zoom_0_125 = 0.0011;
10444 gdouble zoom_factor = zoom_level/0.125;
10446 wh->lat = h_at_zoom_0_125 * zoom_factor;
10447 wh->lon = w_at_zoom_0_125 * zoom_factor;
10449 return 0; /* all OK */
10452 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10454 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10455 (dist->lat >= ABS(to->north_south - from->north_south)))
10458 VikCoord *coord = g_malloc(sizeof(VikCoord));
10459 coord->mode = VIK_COORD_LATLON;
10461 if (ABS(gradient) < 1) {
10462 if (from->east_west > to->east_west)
10463 coord->east_west = from->east_west - dist->lon;
10465 coord->east_west = from->east_west + dist->lon;
10466 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10468 if (from->north_south > to->north_south)
10469 coord->north_south = from->north_south - dist->lat;
10471 coord->north_south = from->north_south + dist->lat;
10472 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10478 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10480 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10481 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10483 VikCoord *next = from;
10485 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10487 list = g_list_prepend(list, next);
10493 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10495 typedef struct _Rect {
10500 #define GLRECT(iter) ((Rect *)((iter)->data))
10503 GList *rects_to_download = NULL;
10506 if (get_download_area_width(vvp, zoom_level, &wh))
10509 GList *iter = tr->trackpoints;
10513 gboolean new_map = TRUE;
10514 VikCoord *cur_coord, tl, br;
10517 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10519 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10520 rect = g_malloc(sizeof(Rect));
10523 rect->center = *cur_coord;
10524 rects_to_download = g_list_prepend(rects_to_download, rect);
10529 gboolean found = FALSE;
10530 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10531 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10542 GList *fillins = NULL;
10543 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10544 /* seems that ATM the function get_next_coord works only for LATLON */
10545 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10546 /* fill-ins for far apart points */
10547 GList *cur_rect, *next_rect;
10548 for (cur_rect = rects_to_download;
10549 (next_rect = cur_rect->next) != NULL;
10550 cur_rect = cur_rect->next) {
10551 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10552 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10553 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10557 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10560 GList *fiter = fillins;
10562 cur_coord = (VikCoord *)(fiter->data);
10563 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10564 rect = g_malloc(sizeof(Rect));
10567 rect->center = *cur_coord;
10568 rects_to_download = g_list_prepend(rects_to_download, rect);
10569 fiter = fiter->next;
10573 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10574 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10578 for (iter = fillins; iter; iter = iter->next)
10579 g_free(iter->data);
10580 g_list_free(fillins);
10582 if (rects_to_download) {
10583 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10584 g_free(rect_iter->data);
10585 g_list_free(rects_to_download);
10589 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10593 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10594 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10595 gint selected_zoom, default_zoom;
10597 VikTrwLayer *vtl = values[MA_VTL];
10598 VikLayersPanel *vlp = values[MA_VLP];
10600 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10601 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10603 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10607 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10609 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10610 int num_maps = g_list_length(vmls);
10613 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10617 // Convert from list of vmls to list of names. Allowing the user to select one of them
10618 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10619 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10621 gchar **np = map_names;
10622 VikMapsLayer **lp = map_layers;
10624 for (i = 0; i < num_maps; i++) {
10625 vml = (VikMapsLayer *)(vmls->data);
10627 *np++ = vik_maps_layer_get_map_label(vml);
10630 // Mark end of the array lists
10634 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10635 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10636 if (cur_zoom == zoom_vals[default_zoom])
10639 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10641 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10644 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10647 for (i = 0; i < num_maps; i++)
10648 g_free(map_names[i]);
10650 g_free(map_layers);
10656 /**** lowest waypoint number calculation ***/
10657 static gint highest_wp_number_name_to_number(const gchar *name) {
10658 if ( strlen(name) == 3 ) {
10659 int n = atoi(name);
10660 if ( n < 100 && name[0] != '0' )
10662 if ( n < 10 && name[0] != '0' )
10670 static void highest_wp_number_reset(VikTrwLayer *vtl)
10672 vtl->highest_wp_number = -1;
10675 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10677 /* if is bigger that top, add it */
10678 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10679 if ( new_wp_num > vtl->highest_wp_number )
10680 vtl->highest_wp_number = new_wp_num;
10683 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10685 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10686 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10687 if ( vtl->highest_wp_number == old_wp_num ) {
10689 vtl->highest_wp_number--;
10691 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10692 /* search down until we find something that *does* exist */
10694 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10695 vtl->highest_wp_number--;
10696 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10701 /* get lowest unused number */
10702 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10705 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10707 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10708 return g_strdup(buf);
10712 * trw_layer_create_track_list_both:
10714 * Create the latest list of tracks and routes
10716 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10718 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10719 GList *tracks = NULL;
10720 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10721 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10723 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10726 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10728 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10730 gchar *title = NULL;
10731 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10732 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10734 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10736 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10740 static void trw_layer_track_list_dialog ( menu_array_layer values )
10742 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10744 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10745 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10749 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10751 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10753 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10754 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );