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 VikTrackpoint *trkpt = vik_track_get_tp_last(tr);
2920 if ( trkpt->has_timestamp ) {
2921 gint dur = ( trkpt->timestamp - (vik_track_get_tp_first(tr)->timestamp) );
2923 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2926 // Get length and consider the appropriate distance units
2927 gdouble tr_len = vik_track_get_length(tr);
2928 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2929 switch (dist_units) {
2930 case VIK_UNITS_DISTANCE_KILOMETRES:
2931 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2933 case VIK_UNITS_DISTANCE_MILES:
2934 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2943 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2945 // Very simple tooltip - may expand detail in the future...
2946 static gchar tmp_buf[32];
2947 g_snprintf (tmp_buf, sizeof(tmp_buf),
2949 g_hash_table_size (l->waypoints));
2953 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2955 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2956 // NB It's OK to return NULL
2961 return w->description;
2970 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2973 * set_statusbar_msg_info_trkpt:
2975 * Function to show track point information on the statusbar
2976 * Items displayed is controlled by the settings format code
2978 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2980 gchar *statusbar_format_code = NULL;
2981 gboolean need2free = FALSE;
2982 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2983 // Otherwise use default
2984 statusbar_format_code = g_strdup ( "KEATDN" );
2988 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2989 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2993 g_free ( statusbar_format_code );
2997 * Function to show basic waypoint information on the statusbar
2999 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3002 switch (a_vik_get_units_height ()) {
3003 case VIK_UNITS_HEIGHT_FEET:
3004 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3007 //VIK_UNITS_HEIGHT_METRES:
3008 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3012 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3013 // one can easily use the current pointer position to see this if needed
3014 gchar *lat = NULL, *lon = NULL;
3015 static struct LatLon ll;
3016 vik_coord_to_latlon (&(wpt->coord), &ll);
3017 a_coords_latlon_to_string ( &ll, &lat, &lon );
3019 // Combine parts to make overall message
3022 // Add comment if available
3023 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3025 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3026 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3033 * General layer selection function, find out which bit is selected and take appropriate action
3035 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3038 l->current_wp = NULL;
3039 l->current_wp_id = NULL;
3040 trw_layer_cancel_current_tp ( l, FALSE );
3043 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3047 case VIK_TREEVIEW_TYPE_LAYER:
3049 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3050 /* Mark for redraw */
3055 case VIK_TREEVIEW_TYPE_SUBLAYER:
3059 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3061 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3062 /* Mark for redraw */
3066 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3068 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3069 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3070 /* Mark for redraw */
3074 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3076 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3077 /* Mark for redraw */
3081 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3083 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3084 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3085 /* Mark for redraw */
3089 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3091 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3092 /* Mark for redraw */
3096 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3098 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3100 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3101 // Show some waypoint info
3102 set_statusbar_msg_info_wpt ( l, wpt );
3103 /* Mark for redraw */
3110 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3119 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3124 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3129 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3134 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3136 return l->waypoints;
3139 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3141 return vtl->tracks_iters;
3144 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3146 return vtl->routes_iters;
3149 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3151 return vtl->waypoints;
3154 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3156 return ! ( g_hash_table_size ( vtl->tracks ) ||
3157 g_hash_table_size ( vtl->routes ) ||
3158 g_hash_table_size ( vtl->waypoints ) );
3161 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3163 return vtl->tracks_visible;
3166 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3168 return vtl->routes_visible;
3171 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3173 return vtl->waypoints_visible;
3177 * ATM use a case sensitive find
3178 * Finds the first one
3180 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3182 if ( wp && wp->name )
3183 if ( ! strcmp ( wp->name, name ) )
3189 * Get waypoint by name - not guaranteed to be unique
3190 * Finds the first one
3192 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3194 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3198 * ATM use a case sensitive find
3199 * Finds the first one
3201 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3203 if ( trk && trk->name )
3204 if ( ! strcmp ( trk->name, name ) )
3210 * Get track by name - not guaranteed to be unique
3211 * Finds the first one
3213 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3215 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3219 * Get route by name - not guaranteed to be unique
3220 * Finds the first one
3222 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3224 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3227 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3229 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3230 maxmin[0].lat = trk->bbox.north;
3231 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3232 maxmin[1].lat = trk->bbox.south;
3233 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3234 maxmin[0].lon = trk->bbox.east;
3235 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3236 maxmin[1].lon = trk->bbox.west;
3239 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3241 // Continually reuse maxmin to find the latest maximum and minimum values
3242 // First set to waypoints bounds
3243 maxmin[0].lat = vtl->waypoints_bbox.north;
3244 maxmin[1].lat = vtl->waypoints_bbox.south;
3245 maxmin[0].lon = vtl->waypoints_bbox.east;
3246 maxmin[1].lon = vtl->waypoints_bbox.west;
3247 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3248 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3251 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3253 /* 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... */
3254 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3255 trw_layer_find_maxmin (vtl, maxmin);
3256 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3260 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3261 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3266 static void trw_layer_centerize ( menu_array_layer values )
3268 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3270 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3271 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3273 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3276 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3278 /* First set the center [in case previously viewing from elsewhere] */
3279 /* Then loop through zoom levels until provided positions are in view */
3280 /* This method is not particularly fast - but should work well enough */
3281 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3283 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3284 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3286 /* Convert into definite 'smallest' and 'largest' positions */
3287 struct LatLon minmin;
3288 if ( maxmin[0].lat < maxmin[1].lat )
3289 minmin.lat = maxmin[0].lat;
3291 minmin.lat = maxmin[1].lat;
3293 struct LatLon maxmax;
3294 if ( maxmin[0].lon > maxmin[1].lon )
3295 maxmax.lon = maxmin[0].lon;
3297 maxmax.lon = maxmin[1].lon;
3299 /* Never zoom in too far - generally not that useful, as too close ! */
3300 /* Always recalculate the 'best' zoom level */
3302 vik_viewport_set_zoom ( vvp, zoom );
3304 gdouble min_lat, max_lat, min_lon, max_lon;
3305 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3306 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3307 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3308 /* NB I think the logic used in this test to determine if the bounds is within view
3309 fails if track goes across 180 degrees longitude.
3310 Hopefully that situation is not too common...
3311 Mind you viking doesn't really do edge locations to well anyway */
3312 if ( min_lat < minmin.lat &&
3313 max_lat > minmin.lat &&
3314 min_lon < maxmax.lon &&
3315 max_lon > maxmax.lon )
3316 /* Found within zoom level */
3321 vik_viewport_set_zoom ( vvp, zoom );
3325 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3327 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3328 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3329 trw_layer_find_maxmin (vtl, maxmin);
3330 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3333 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3338 static void trw_layer_auto_view ( menu_array_layer values )
3340 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3341 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3342 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3343 vik_layers_panel_emit_update ( vlp );
3346 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3349 static void trw_layer_export_gpspoint ( menu_array_layer values )
3351 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3353 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3355 g_free ( auto_save_name );
3358 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3360 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3362 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3364 g_free ( auto_save_name );
3367 static void trw_layer_export_gpx ( menu_array_layer values )
3369 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3371 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3373 g_free ( auto_save_name );
3376 static void trw_layer_export_kml ( menu_array_layer values )
3378 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3380 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3382 g_free ( auto_save_name );
3385 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3387 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3388 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3391 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3393 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3396 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3398 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3401 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3403 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3405 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3406 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3408 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3410 if ( !trk || !trk->name )
3413 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3415 gchar *label = NULL;
3416 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3417 label = _("Export Route as GPX");
3419 label = _("Export Track as GPX");
3420 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3422 g_free ( auto_save_name );
3425 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3427 wpu_udata *user_data = udata;
3428 if ( wp == user_data->wp ) {
3429 user_data->uuid = id;
3435 static void trw_layer_goto_wp ( menu_array_layer values )
3437 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3438 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3439 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3440 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3441 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3443 GTK_RESPONSE_REJECT,
3445 GTK_RESPONSE_ACCEPT,
3448 GtkWidget *label, *entry;
3449 label = gtk_label_new(_("Waypoint Name:"));
3450 entry = gtk_entry_new();
3452 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3453 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3454 gtk_widget_show_all ( label );
3455 gtk_widget_show_all ( entry );
3457 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3459 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3461 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3462 // Find *first* wp with the given name
3463 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3466 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3469 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3470 vik_layers_panel_emit_update ( vlp );
3472 // Find and select on the side panel
3477 // Hmmm, want key of it
3478 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3480 if ( wpf && udata.uuid ) {
3481 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3482 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3491 gtk_widget_destroy ( dia );
3494 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3496 gchar *default_name = highest_wp_number_get(vtl);
3497 VikWaypoint *wp = vik_waypoint_new();
3498 gchar *returned_name;
3500 wp->coord = *def_coord;
3502 // Attempt to auto set height if DEM data is available
3503 vik_waypoint_apply_dem_data ( wp, TRUE );
3505 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3507 if ( returned_name )
3510 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3511 g_free (default_name);
3512 g_free (returned_name);
3515 g_free (default_name);
3516 vik_waypoint_free(wp);
3520 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3522 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3523 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3524 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3525 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3526 VikViewport *vvp = vik_window_viewport(vw);
3528 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3529 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3530 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3531 trw_layer_calculate_bounds_waypoints ( vtl );
3532 vik_layers_panel_emit_update ( vlp );
3535 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3537 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3538 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3539 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3541 trw_layer_find_maxmin (vtl, maxmin);
3542 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3543 trw_layer_calculate_bounds_waypoints ( vtl );
3544 vik_layers_panel_emit_update ( vlp );
3547 #ifdef VIK_CONFIG_GEOTAG
3548 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3550 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3552 // Update directly - not changing the mtime
3553 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3556 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3558 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3561 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3565 * Use code in separate file for this feature as reasonably complex
3567 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3569 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3570 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3571 // Unset so can be reverified later if necessary
3572 vtl->has_verified_thumbnails = FALSE;
3574 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3580 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3582 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3583 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3585 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3591 static void trw_layer_geotagging ( menu_array_layer values )
3593 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3594 // Unset so can be reverified later if necessary
3595 vtl->has_verified_thumbnails = FALSE;
3597 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3604 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3606 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3608 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3609 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3610 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3611 VikViewport *vvp = vik_window_viewport(vw);
3613 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3617 * Acquire into this TRW Layer straight from GPS Device
3619 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3621 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3622 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3626 * Acquire into this TRW Layer from Directions
3628 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3630 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3634 * Acquire into this TRW Layer from an entered URL
3636 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3638 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3639 trw_layer_acquire ( values, &vik_datasource_url_interface );
3642 #ifdef VIK_CONFIG_OPENSTREETMAP
3644 * Acquire into this TRW Layer from OSM
3646 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3648 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3652 * Acquire into this TRW Layer from OSM for 'My' Traces
3654 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3656 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3660 #ifdef VIK_CONFIG_GEOCACHES
3662 * Acquire into this TRW Layer from Geocaching.com
3664 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3666 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3670 #ifdef VIK_CONFIG_GEOTAG
3672 * Acquire into this TRW Layer from images
3674 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3676 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3678 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3679 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3681 // Reverify thumbnails as they may have changed
3682 vtl->has_verified_thumbnails = FALSE;
3683 trw_layer_verify_thumbnails ( vtl, NULL );
3687 static void trw_layer_gps_upload ( menu_array_layer values )
3689 menu_array_sublayer data;
3691 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3693 data[MA_VTL] = values[MA_VTL];
3694 data[MA_VLP] = values[MA_VLP];
3696 trw_layer_gps_upload_any ( data );
3700 * If pass_along[3] is defined that this will upload just that track
3702 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3704 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3705 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3707 // May not actually get a track here as values[2&3] can be null
3708 VikTrack *track = NULL;
3709 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3710 gboolean xfer_all = FALSE;
3712 if ( values[MA_SUBTYPE] ) {
3714 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3715 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3718 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3719 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3722 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3725 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3729 else if ( !values[MA_CONFIRM] )
3730 xfer_all = TRUE; // i.e. whole layer
3732 if (track && !track->visible) {
3733 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3737 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3738 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3739 GTK_DIALOG_DESTROY_WITH_PARENT,
3741 GTK_RESPONSE_ACCEPT,
3743 GTK_RESPONSE_REJECT,
3746 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3747 GtkWidget *response_w = NULL;
3748 #if GTK_CHECK_VERSION (2, 20, 0)
3749 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3753 gtk_widget_grab_focus ( response_w );
3755 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3757 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3758 datasource_gps_clean_up ( dgs );
3759 gtk_widget_destroy ( dialog );
3763 // Get info from reused datasource dialog widgets
3764 gchar* protocol = datasource_gps_get_protocol ( dgs );
3765 gchar* port = datasource_gps_get_descriptor ( dgs );
3766 // NB don't free the above strings as they're references to values held elsewhere
3767 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3768 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3769 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3770 gboolean turn_off = datasource_gps_get_off ( dgs );
3772 gtk_widget_destroy ( dialog );
3774 // When called from the viewport - work the corresponding layerspanel:
3776 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3779 // Apply settings to transfer to the GPS device
3786 vik_layers_panel_get_viewport (vlp),
3795 * Acquire into this TRW Layer from any GPS Babel supported file
3797 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3799 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3800 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3801 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3802 VikViewport *vvp = vik_window_viewport(vw);
3804 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3807 static void trw_layer_new_wp ( menu_array_layer values )
3809 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3810 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3811 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3812 instead return true if you want to update. */
3813 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 ) {
3814 trw_layer_calculate_bounds_waypoints ( vtl );
3815 vik_layers_panel_emit_update ( vlp );
3819 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3821 vtl->current_track = vik_track_new();
3822 vik_track_set_defaults ( vtl->current_track );
3823 vtl->current_track->visible = TRUE;
3824 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3825 // Create track with the preferred colour from the layer properties
3826 vtl->current_track->color = vtl->track_color;
3828 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3829 vtl->current_track->has_color = TRUE;
3830 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3833 static void trw_layer_new_track ( menu_array_layer values )
3835 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3837 if ( ! vtl->current_track ) {
3838 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3839 new_track_create_common ( vtl, name );
3842 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3846 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3848 vtl->current_track = vik_track_new();
3849 vik_track_set_defaults ( vtl->current_track );
3850 vtl->current_track->visible = TRUE;
3851 vtl->current_track->is_route = TRUE;
3852 // By default make all routes red
3853 vtl->current_track->has_color = TRUE;
3854 gdk_color_parse ( "red", &vtl->current_track->color );
3855 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3858 static void trw_layer_new_route ( menu_array_layer values )
3860 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3862 if ( ! vtl->current_track ) {
3863 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3864 new_route_create_common ( vtl, name );
3866 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3870 static void trw_layer_auto_routes_view ( menu_array_layer values )
3872 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3873 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3875 if ( g_hash_table_size (vtl->routes) > 0 ) {
3876 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3877 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3878 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3879 vik_layers_panel_emit_update ( vlp );
3884 static void trw_layer_finish_track ( menu_array_layer values )
3886 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3887 vtl->current_track = NULL;
3888 vtl->route_finder_started = FALSE;
3889 vik_layer_emit_update ( VIK_LAYER(vtl) );
3892 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3894 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3895 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3897 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3898 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3899 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3900 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3901 vik_layers_panel_emit_update ( vlp );
3905 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3907 /* NB do not care if wp is visible or not */
3908 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3911 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3913 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3914 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3916 /* Only 1 waypoint - jump straight to it */
3917 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3918 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3919 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3921 /* If at least 2 waypoints - find center and then zoom to fit */
3922 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3924 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3925 maxmin[0].lat = vtl->waypoints_bbox.north;
3926 maxmin[1].lat = vtl->waypoints_bbox.south;
3927 maxmin[0].lon = vtl->waypoints_bbox.east;
3928 maxmin[1].lon = vtl->waypoints_bbox.west;
3929 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3932 vik_layers_panel_emit_update ( vlp );
3935 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3937 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3940 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3942 if ( values[MA_MISC] ) {
3943 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3944 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3948 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3950 static menu_array_layer pass_along;
3952 GtkWidget *export_submenu;
3953 pass_along[MA_VTL] = vtl;
3954 pass_along[MA_VLP] = vlp;
3956 item = gtk_menu_item_new();
3957 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3958 gtk_widget_show ( item );
3960 if ( vtl->current_track ) {
3961 if ( vtl->current_track->is_route )
3962 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3964 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3966 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3967 gtk_widget_show ( item );
3970 item = gtk_menu_item_new ();
3971 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3972 gtk_widget_show ( item );
3975 /* Now with icons */
3976 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3979 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3980 gtk_widget_show ( item );
3982 GtkWidget *view_submenu = gtk_menu_new();
3983 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3985 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3986 gtk_widget_show ( item );
3987 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3989 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3991 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3992 gtk_widget_show ( item );
3994 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3996 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3997 gtk_widget_show ( item );
3999 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4001 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4002 gtk_widget_show ( item );
4004 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4005 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4007 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4008 gtk_widget_show ( item );
4010 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4011 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4012 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4013 gtk_widget_show ( item );
4015 export_submenu = gtk_menu_new ();
4016 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4018 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4019 gtk_widget_show ( item );
4020 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4022 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4024 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4025 gtk_widget_show ( item );
4027 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4029 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4030 gtk_widget_show ( item );
4032 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4034 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4035 gtk_widget_show ( item );
4037 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4039 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4040 gtk_widget_show ( item );
4042 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4044 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4045 gtk_widget_show ( item );
4047 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4048 item = gtk_menu_item_new_with_mnemonic ( external1 );
4049 g_free ( external1 );
4050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4051 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4052 gtk_widget_show ( item );
4054 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4055 item = gtk_menu_item_new_with_mnemonic ( external2 );
4056 g_free ( external2 );
4057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4058 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4059 gtk_widget_show ( item );
4061 GtkWidget *new_submenu = gtk_menu_new();
4062 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4063 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4064 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4065 gtk_widget_show(item);
4066 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4068 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4071 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4072 gtk_widget_show ( item );
4074 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4077 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4078 gtk_widget_show ( item );
4079 // Make it available only when a new track *not* already in progress
4080 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4082 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4083 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4085 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4086 gtk_widget_show ( item );
4087 // Make it available only when a new track *not* already in progress
4088 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4090 #ifdef VIK_CONFIG_GEOTAG
4091 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4092 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4093 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4094 gtk_widget_show ( item );
4097 GtkWidget *acquire_submenu = gtk_menu_new ();
4098 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4100 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4101 gtk_widget_show ( item );
4102 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4104 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4106 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4107 gtk_widget_show ( item );
4109 /* FIXME: only add menu when at least a routing engine has support for Directions */
4110 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4111 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4112 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4113 gtk_widget_show ( item );
4115 #ifdef VIK_CONFIG_OPENSTREETMAP
4116 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4118 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4119 gtk_widget_show ( item );
4121 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4123 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4124 gtk_widget_show ( item );
4127 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4128 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4129 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4130 gtk_widget_show ( item );
4132 #ifdef VIK_CONFIG_GEONAMES
4133 GtkWidget *wikipedia_submenu = gtk_menu_new();
4134 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4135 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4136 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4137 gtk_widget_show(item);
4138 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4140 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4144 gtk_widget_show ( item );
4146 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4147 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4149 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4150 gtk_widget_show ( item );
4153 #ifdef VIK_CONFIG_GEOCACHES
4154 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4155 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4156 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4157 gtk_widget_show ( item );
4160 #ifdef VIK_CONFIG_GEOTAG
4161 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4163 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4164 gtk_widget_show ( item );
4167 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4169 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4170 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4171 gtk_widget_show ( item );
4173 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4175 GtkWidget *upload_submenu = gtk_menu_new ();
4176 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4178 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4179 gtk_widget_show ( item );
4180 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4182 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4183 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4185 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4186 gtk_widget_show ( item );
4188 #ifdef VIK_CONFIG_OPENSTREETMAP
4189 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4190 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4192 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4193 gtk_widget_show ( item );
4196 GtkWidget *delete_submenu = gtk_menu_new ();
4197 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4198 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4199 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4200 gtk_widget_show ( item );
4201 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4203 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4204 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4206 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4207 gtk_widget_show ( item );
4209 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4210 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4211 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4212 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4213 gtk_widget_show ( item );
4215 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4216 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4217 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4218 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4219 gtk_widget_show ( item );
4221 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4222 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4223 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4224 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4225 gtk_widget_show ( item );
4227 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4229 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4230 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4231 gtk_widget_show ( item );
4233 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4234 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4236 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4237 gtk_widget_show ( item );
4239 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4240 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4242 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4243 gtk_widget_show ( item );
4246 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4247 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4249 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4250 gtk_widget_show ( item );
4253 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4254 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4256 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4257 gtk_widget_show ( item );
4258 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4260 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4261 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4262 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4263 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4264 gtk_widget_show ( item );
4265 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4268 // Fake Waypoint UUIDs vi simple increasing integer
4269 static guint wp_uuid = 0;
4271 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4275 vik_waypoint_set_name (wp, name);
4277 if ( VIK_LAYER(vtl)->realized )
4279 // Do we need to create the sublayer:
4280 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4281 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4284 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4286 // Visibility column always needed for waypoints
4287 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 );
4289 // Actual setting of visibility dependent on the waypoint
4290 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4292 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4294 // Sort now as post_read is not called on a realized waypoint
4295 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4298 highest_wp_number_add_wp(vtl, name);
4299 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4303 // Fake Track UUIDs vi simple increasing integer
4304 static guint tr_uuid = 0;
4306 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4310 vik_track_set_name (t, name);
4312 if ( VIK_LAYER(vtl)->realized )
4314 // Do we need to create the sublayer:
4315 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4316 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4319 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4320 // Visibility column always needed for tracks
4321 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 );
4323 // Actual setting of visibility dependent on the track
4324 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4326 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4328 // Sort now as post_read is not called on a realized track
4329 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4332 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4334 trw_layer_update_treeview ( vtl, t );
4337 // Fake Route UUIDs vi simple increasing integer
4338 static guint rt_uuid = 0;
4340 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4344 vik_track_set_name (t, name);
4346 if ( VIK_LAYER(vtl)->realized )
4348 // Do we need to create the sublayer:
4349 if ( g_hash_table_size (vtl->routes) == 0 ) {
4350 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4353 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4354 // Visibility column always needed for routes
4355 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 );
4356 // Actual setting of visibility dependent on the route
4357 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4359 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4361 // Sort now as post_read is not called on a realized route
4362 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4365 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4367 trw_layer_update_treeview ( vtl, t );
4370 /* to be called whenever a track has been deleted or may have been changed. */
4371 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4373 if (vtl->current_tp_track == trk )
4374 trw_layer_cancel_current_tp ( vtl, FALSE );
4378 * Normally this is done to due the waypoint size preference having changed
4380 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4382 GHashTableIter iter;
4383 gpointer key, value;
4386 g_hash_table_iter_init ( &iter, vtl->waypoints );
4387 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4388 VikWaypoint *wp = VIK_WAYPOINT(value);
4390 // Reapply symbol setting to update the pixbuf
4391 gchar *tmp_symbol = g_strdup ( wp->symbol );
4392 vik_waypoint_set_symbol ( wp, tmp_symbol );
4393 g_free ( tmp_symbol );
4399 * trw_layer_new_unique_sublayer_name:
4401 * Allocates a unique new name
4403 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4406 gchar *newname = g_strdup(name);
4411 switch ( sublayer_type ) {
4412 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4413 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4415 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4416 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4419 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4422 // If found a name already in use try adding 1 to it and we try again
4424 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4426 newname = new_newname;
4429 } while ( id != NULL);
4434 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4436 // No more uniqueness of name forced when loading from a file
4437 // This now makes this function a little redunant as we just flow the parameters through
4438 vik_trw_layer_add_waypoint ( vtl, name, wp );
4441 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4443 if ( vtl->route_finder_append && vtl->current_track ) {
4444 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4446 // enforce end of current track equal to start of tr
4447 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4448 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4449 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4450 vik_track_add_trackpoint ( vtl->current_track,
4451 vik_trackpoint_copy ( cur_end ),
4455 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4456 vik_track_free ( tr );
4457 vtl->route_finder_append = FALSE; /* this means we have added it */
4460 // No more uniqueness of name forced when loading from a file
4462 vik_trw_layer_add_route ( vtl, name, tr );
4464 vik_trw_layer_add_track ( vtl, name, tr );
4466 if ( vtl->route_finder_check_added_track ) {
4467 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4468 vtl->route_finder_added_track = tr;
4473 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4475 *l = g_list_append(*l, id);
4479 * Move an item from one TRW layer to another TRW layer
4481 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4483 // TODO reconsider strategy when moving within layer (if anything...)
4484 gboolean rename = ( vtl_src != vtl_dest );
4488 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4489 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4493 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4495 newname = g_strdup ( trk->name );
4497 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4498 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4500 vik_trw_layer_delete_track ( vtl_src, trk );
4503 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4504 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4508 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4510 newname = g_strdup ( trk->name );
4512 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4513 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4515 vik_trw_layer_delete_route ( vtl_src, trk );
4518 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4519 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4523 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4525 newname = g_strdup ( wp->name );
4527 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4528 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4530 trw_layer_delete_waypoint ( vtl_src, wp );
4532 // Recalculate bounds even if not renamed as maybe dragged between layers
4533 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4534 trw_layer_calculate_bounds_waypoints ( vtl_src );
4538 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4540 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4541 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4543 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4544 GList *items = NULL;
4547 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4548 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4550 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4551 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4553 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4554 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4559 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4560 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4561 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4562 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4564 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4571 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4572 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4576 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4578 trku_udata *user_data = udata;
4579 if ( trk == user_data->trk ) {
4580 user_data->uuid = id;
4586 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4588 gboolean was_visible = FALSE;
4589 if ( trk && trk->name ) {
4591 if ( trk == vtl->current_track ) {
4592 vtl->current_track = NULL;
4593 vtl->current_tp_track = NULL;
4594 vtl->current_tp_id = NULL;
4595 vtl->moving_tp = FALSE;
4596 vtl->route_finder_started = FALSE;
4599 was_visible = trk->visible;
4601 if ( trk == vtl->route_finder_added_track )
4602 vtl->route_finder_added_track = NULL;
4608 // Hmmm, want key of it
4609 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4611 if ( trkf && udata.uuid ) {
4612 /* could be current_tp, so we have to check */
4613 trw_layer_cancel_tps_of_track ( vtl, trk );
4615 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4618 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4619 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4620 g_hash_table_remove ( vtl->tracks, udata.uuid );
4622 // If last sublayer, then remove sublayer container
4623 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4624 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4627 // Incase it was selected (no item delete signal ATM)
4628 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4634 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4636 gboolean was_visible = FALSE;
4638 if ( trk && trk->name ) {
4640 if ( trk == vtl->current_track ) {
4641 vtl->current_track = NULL;
4642 vtl->current_tp_track = NULL;
4643 vtl->current_tp_id = NULL;
4644 vtl->moving_tp = FALSE;
4647 was_visible = trk->visible;
4649 if ( trk == vtl->route_finder_added_track )
4650 vtl->route_finder_added_track = NULL;
4656 // Hmmm, want key of it
4657 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4659 if ( trkf && udata.uuid ) {
4660 /* could be current_tp, so we have to check */
4661 trw_layer_cancel_tps_of_track ( vtl, trk );
4663 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4666 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4667 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4668 g_hash_table_remove ( vtl->routes, udata.uuid );
4670 // If last sublayer, then remove sublayer container
4671 if ( g_hash_table_size (vtl->routes) == 0 ) {
4672 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4675 // Incase it was selected (no item delete signal ATM)
4676 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4682 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4684 gboolean was_visible = FALSE;
4686 if ( wp && wp->name ) {
4688 if ( wp == vtl->current_wp ) {
4689 vtl->current_wp = NULL;
4690 vtl->current_wp_id = NULL;
4691 vtl->moving_wp = FALSE;
4694 was_visible = wp->visible;
4700 // Hmmm, want key of it
4701 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4703 if ( wpf && udata.uuid ) {
4704 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4707 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4708 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4710 highest_wp_number_remove_wp(vtl, wp->name);
4711 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4713 // If last sublayer, then remove sublayer container
4714 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4715 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4718 // Incase it was selected (no item delete signal ATM)
4719 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4727 // Only for temporary use by trw_layer_delete_waypoint_by_name
4728 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4730 wpu_udata *user_data = udata;
4731 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4732 user_data->uuid = id;
4739 * Delete a waypoint by the given name
4740 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4741 * as there be multiple waypoints with the same name
4743 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4746 // Fake a waypoint with the given name
4747 udata.wp = vik_waypoint_new ();
4748 vik_waypoint_set_name (udata.wp, name);
4749 // Currently only the name is used in this waypoint find function
4752 // Hmmm, want key of it
4753 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4755 vik_waypoint_free (udata.wp);
4757 if ( wpf && udata.uuid )
4758 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4764 VikTrack *trk; // input
4765 gpointer uuid; // output
4768 // Only for temporary use by trw_layer_delete_track_by_name
4769 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4771 tpu_udata *user_data = udata;
4772 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4773 user_data->uuid = id;
4780 * Delete a track by the given name
4781 * NOTE: ATM this will delete the first encountered Track with the specified name
4782 * as there may be multiple tracks with the same name within the specified hash table
4784 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4787 // Fake a track with the given name
4788 udata.trk = vik_track_new ();
4789 vik_track_set_name (udata.trk, name);
4790 // Currently only the name is used in this waypoint find function
4793 // Hmmm, want key of it
4794 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4796 vik_track_free (udata.trk);
4798 if ( trkf && udata.uuid ) {
4799 // This could be a little better written...
4800 if ( vtl->tracks == ht_tracks )
4801 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4802 if ( vtl->routes == ht_tracks )
4803 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4810 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4812 vik_treeview_item_delete (vt, it );
4815 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4818 vtl->current_track = NULL;
4819 vtl->route_finder_added_track = NULL;
4820 if (vtl->current_tp_track)
4821 trw_layer_cancel_current_tp(vtl, FALSE);
4823 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4824 g_hash_table_remove_all(vtl->routes_iters);
4825 g_hash_table_remove_all(vtl->routes);
4827 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4829 vik_layer_emit_update ( VIK_LAYER(vtl) );
4832 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4835 vtl->current_track = NULL;
4836 vtl->route_finder_added_track = NULL;
4837 if (vtl->current_tp_track)
4838 trw_layer_cancel_current_tp(vtl, FALSE);
4840 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4841 g_hash_table_remove_all(vtl->tracks_iters);
4842 g_hash_table_remove_all(vtl->tracks);
4844 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4846 vik_layer_emit_update ( VIK_LAYER(vtl) );
4849 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4851 vtl->current_wp = NULL;
4852 vtl->current_wp_id = NULL;
4853 vtl->moving_wp = FALSE;
4855 highest_wp_number_reset(vtl);
4857 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4858 g_hash_table_remove_all(vtl->waypoints_iters);
4859 g_hash_table_remove_all(vtl->waypoints);
4861 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4863 vik_layer_emit_update ( VIK_LAYER(vtl) );
4866 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4868 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4869 // Get confirmation from the user
4870 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4871 _("Are you sure you want to delete all tracks in %s?"),
4872 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4873 vik_trw_layer_delete_all_tracks (vtl);
4876 static void trw_layer_delete_all_routes ( menu_array_layer values )
4878 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4879 // Get confirmation from the user
4880 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4881 _("Are you sure you want to delete all routes in %s?"),
4882 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4883 vik_trw_layer_delete_all_routes (vtl);
4886 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4888 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4889 // Get confirmation from the user
4890 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4891 _("Are you sure you want to delete all waypoints in %s?"),
4892 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4893 vik_trw_layer_delete_all_waypoints (vtl);
4896 static void trw_layer_delete_item ( menu_array_sublayer values )
4898 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4899 gboolean was_visible = FALSE;
4900 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4902 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4903 if ( wp && wp->name ) {
4904 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4905 // Get confirmation from the user
4906 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4907 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4908 _("Are you sure you want to delete the waypoint \"%s\"?"),
4911 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4914 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4916 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4917 if ( trk && trk->name ) {
4918 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4919 // Get confirmation from the user
4920 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4921 _("Are you sure you want to delete the track \"%s\"?"),
4924 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4929 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4930 if ( trk && trk->name ) {
4931 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4932 // Get confirmation from the user
4933 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4934 _("Are you sure you want to delete the route \"%s\"?"),
4937 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4941 vik_layer_emit_update ( VIK_LAYER(vtl) );
4945 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4947 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4949 vik_waypoint_set_name ( wp, new_name );
4951 // Now update the treeview as well
4956 // Need key of it for treeview update
4957 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4959 if ( wpf && udataU.uuid ) {
4960 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4963 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4964 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4970 * Maintain icon of waypoint in the treeview
4972 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4974 // update the treeview
4979 // Need key of it for treeview update
4980 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4982 if ( wpf && udataU.uuid ) {
4983 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4986 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4991 static void trw_layer_properties_item ( menu_array_sublayer values )
4993 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4994 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4996 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4998 if ( wp && wp->name )
5000 gboolean updated = FALSE;
5001 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5003 trw_layer_waypoint_rename ( vtl, wp, new_name );
5005 if ( updated && values[MA_TV_ITER] )
5006 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5008 if ( updated && VIK_LAYER(vtl)->visible )
5009 vik_layer_emit_update ( VIK_LAYER(vtl) );
5015 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5016 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5018 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5020 if ( tr && tr->name )
5022 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5033 * trw_layer_track_statistics:
5035 * Show track statistics.
5036 * ATM jump to the stats page in the properties
5037 * TODO: consider separating the stats into an individual dialog?
5039 static void trw_layer_track_statistics ( menu_array_sublayer values )
5041 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5043 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5044 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5046 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5048 if ( trk && trk->name ) {
5049 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5059 * Update the treeview of the track id - primarily to update the icon
5061 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5067 gpointer *trkf = NULL;
5068 if ( trk->is_route )
5069 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5071 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5073 if ( trkf && udata.uuid ) {
5075 GtkTreeIter *iter = NULL;
5076 if ( trk->is_route )
5077 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5079 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5082 // TODO: Make this a function
5083 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5084 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5085 ((trk->color.green & 0xff00) << 8) |
5086 (trk->color.blue & 0xff00);
5087 gdk_pixbuf_fill ( pixbuf, pixel );
5088 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5089 g_object_unref (pixbuf);
5096 Parameter 1 -> VikLayersPanel
5097 Parameter 2 -> VikLayer
5098 Parameter 3 -> VikViewport
5100 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5103 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5104 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5107 /* since vlp not set, vl & vvp should be valid instead! */
5109 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5110 vik_layer_emit_update ( VIK_LAYER(vl) );
5115 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5117 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5119 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5120 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5122 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5124 if ( track && track->trackpoints )
5125 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5128 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5130 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5132 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5133 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5135 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5137 if ( track && track->trackpoints )
5139 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5141 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5142 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5143 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5144 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5145 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5149 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5151 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5153 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5154 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5156 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5161 // Converting a track to a route can be a bit more complicated,
5162 // so give a chance to change our minds:
5163 if ( !trk->is_route &&
5164 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5165 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5167 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5168 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5173 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5176 trk_copy->is_route = !trk_copy->is_route;
5178 // ATM can't set name to self - so must create temporary copy
5179 gchar *name = g_strdup ( trk_copy->name );
5181 // Delete old one and then add new one
5182 if ( trk->is_route ) {
5183 vik_trw_layer_delete_route ( vtl, trk );
5184 vik_trw_layer_add_track ( vtl, name, trk_copy );
5187 // Extra route conversion bits...
5188 vik_track_merge_segments ( trk_copy );
5189 vik_track_to_routepoints ( trk_copy );
5191 vik_trw_layer_delete_track ( vtl, trk );
5192 vik_trw_layer_add_route ( vtl, name, trk_copy );
5196 // Update in case color of track / route changes when moving between sublayers
5197 vik_layer_emit_update ( VIK_LAYER(vtl) );
5200 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5202 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5204 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5205 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5207 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5210 vik_track_anonymize_times ( track );
5213 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5215 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5217 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5218 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5220 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5225 vtl->current_track = track;
5226 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);
5228 if ( track->trackpoints )
5229 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5233 * extend a track using route finder
5235 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5237 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5238 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5242 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5243 vtl->current_track = track;
5244 vtl->route_finder_started = TRUE;
5246 if ( track->trackpoints )
5247 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5253 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5255 // If have a vlp then perform a basic test to see if any DEM info available...
5257 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5259 if ( !g_list_length(dems) ) {
5260 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5268 * apply_dem_data_common:
5270 * A common function for applying the DEM values and reporting the results.
5272 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5274 if ( !trw_layer_dem_test ( vtl, vlp ) )
5277 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5278 // Inform user how much was changed
5280 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5281 g_snprintf(str, 64, tmp_str, changed);
5282 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5285 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5287 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5289 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5290 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5292 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5295 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5298 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5300 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5302 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5303 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5305 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5308 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5314 * A common function for applying the elevation smoothing and reporting the results.
5316 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5318 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5319 // Inform user how much was changed
5321 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5322 g_snprintf(str, 64, tmp_str, changed);
5323 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5329 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5331 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5333 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5334 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5336 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5341 smooth_it ( vtl, track, FALSE );
5344 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5346 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5348 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5349 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5351 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5356 smooth_it ( vtl, track, TRUE );
5360 * Commonal helper function
5362 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5365 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5366 g_snprintf(str, 64, tmp_str, changed);
5367 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5370 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5372 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5373 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5375 if ( !trw_layer_dem_test ( vtl, vlp ) )
5379 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5381 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5383 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5387 GHashTableIter iter;
5388 gpointer key, value;
5390 g_hash_table_iter_init ( &iter, vtl->waypoints );
5391 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5392 VikWaypoint *wp = VIK_WAYPOINT(value);
5393 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5396 wp_changed_message ( vtl, changed );
5399 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5401 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5402 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5404 if ( !trw_layer_dem_test ( vtl, vlp ) )
5408 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5410 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5412 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5416 GHashTableIter iter;
5417 gpointer key, value;
5419 g_hash_table_iter_init ( &iter, vtl->waypoints );
5420 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5421 VikWaypoint *wp = VIK_WAYPOINT(value);
5422 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5425 wp_changed_message ( vtl, changed );
5428 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5430 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5432 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5433 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5435 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5439 if ( !track->trackpoints )
5441 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5444 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5446 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5448 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5449 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5451 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5456 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5459 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5462 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5464 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5466 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5467 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5469 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5474 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5477 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5480 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5482 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5484 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5485 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5487 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5492 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5495 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5499 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5501 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5503 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5505 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5506 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5508 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5510 if ( trk && trk->trackpoints )
5512 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5513 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5514 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5515 if ( values[MA_VLP] )
5516 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5518 vik_layer_emit_update ( VIK_LAYER(vtl) );
5523 * Refine the selected track/route with a routing engine.
5524 * The routing engine is selected by the user, when requestiong the job.
5526 static void trw_layer_route_refine ( menu_array_sublayer values )
5528 static gint last_engine = 0;
5529 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5532 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5533 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5535 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5537 if ( trk && trk->trackpoints )
5539 /* Check size of the route */
5540 int nb = vik_track_get_tp_count(trk);
5542 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5543 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5544 GTK_MESSAGE_WARNING,
5545 GTK_BUTTONS_OK_CANCEL,
5546 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5548 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5549 gtk_widget_destroy ( dialog );
5550 if (response != GTK_RESPONSE_OK )
5553 /* Select engine from dialog */
5554 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5555 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5556 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5558 GTK_RESPONSE_REJECT,
5560 GTK_RESPONSE_ACCEPT,
5562 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5563 gtk_widget_show_all(label);
5565 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5567 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5568 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5569 gtk_widget_show_all(combo);
5571 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5573 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5575 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5577 /* Dialog validated: retrieve selected engine and do the job */
5578 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5579 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5582 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5584 /* Force saving track */
5585 /* FIXME: remove or rename this hack */
5586 vtl->route_finder_check_added_track = TRUE;
5589 vik_routing_engine_refine (routing, vtl, trk);
5591 /* FIXME: remove or rename this hack */
5592 if ( vtl->route_finder_added_track )
5593 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5595 vtl->route_finder_added_track = NULL;
5596 vtl->route_finder_check_added_track = FALSE;
5598 vik_layer_emit_update ( VIK_LAYER(vtl) );
5600 /* Restore cursor */
5601 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5603 gtk_widget_destroy ( dialog );
5607 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5609 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5610 trw_layer_tpwin_init ( vtl );
5613 /*************************************
5614 * merge/split by time routines
5615 *************************************/
5617 /* called for each key in track hash table.
5618 * If the current track has the same time stamp type, add it to the result,
5619 * except the one pointed by "exclude".
5620 * set exclude to NULL if there is no exclude to check.
5621 * Note that the result is in reverse (for performance reasons).
5626 gboolean with_timestamps;
5628 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5630 twt_udata *user_data = udata;
5631 VikTrackpoint *p1, *p2;
5632 VikTrack *trk = VIK_TRACK(value);
5633 if (trk == user_data->exclude) {
5637 if (trk->trackpoints) {
5638 p1 = vik_track_get_tp_first(trk);
5639 p2 = vik_track_get_tp_last(trk);
5641 if ( user_data->with_timestamps ) {
5642 if (!p1->has_timestamp || !p2->has_timestamp) {
5647 // Don't add tracks with timestamps when getting non timestamp tracks
5648 if (p1->has_timestamp || p2->has_timestamp) {
5654 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5658 * find_nearby_tracks_by_time:
5660 * Called for each track in track hash table.
5661 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5662 * to the current track, then the current track is added to the list in user_data[0]
5664 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5666 VikTrack *trk = VIK_TRACK(value);
5668 GList **nearby_tracks = ((gpointer *)user_data)[0];
5669 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5671 if ( !orig_trk || !orig_trk->trackpoints )
5675 * detect reasons for not merging, and return
5676 * if no reason is found not to merge, then do it.
5679 twt_udata *udata = user_data;
5680 // Exclude the original track from the compiled list
5681 if (trk == udata->exclude) {
5685 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5686 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5688 if (trk->trackpoints) {
5690 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5691 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5693 if (!p1->has_timestamp || !p2->has_timestamp) {
5694 //g_print("no timestamp\n");
5698 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5699 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5700 if (! (abs(t1 - p2->timestamp) < threshold ||
5702 abs(p1->timestamp - t2) < threshold)
5709 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5712 /* comparison function used to sort tracks; a and b are hash table keys */
5713 /* Not actively used - can be restored if needed
5714 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5716 GHashTable *tracks = user_data;
5719 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5720 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5722 if (t1 < t2) return -1;
5723 if (t1 > t2) return 1;
5728 /* comparison function used to sort trackpoints */
5729 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5731 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5733 if (t1 < t2) return -1;
5734 if (t1 > t2) return 1;
5739 * comparison function which can be used to sort tracks or waypoints by name
5741 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5743 const gchar* namea = (const gchar*) a;
5744 const gchar* nameb = (const gchar*) b;
5745 if ( namea == NULL || nameb == NULL)
5748 // Same sort method as used in the vik_treeview_*_alphabetize functions
5749 return strcmp ( namea, nameb );
5753 * Attempt to merge selected track with other tracks specified by the user
5754 * Tracks to merge with must be of the same 'type' as the selected track -
5755 * either all with timestamps, or all without timestamps
5757 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5759 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5760 GList *other_tracks = NULL;
5761 GHashTable *ght_tracks;
5762 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5763 ght_tracks = vtl->routes;
5765 ght_tracks = vtl->tracks;
5767 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5772 if ( !track->trackpoints )
5776 udata.result = &other_tracks;
5777 udata.exclude = track;
5778 // Allow merging with 'similar' time type time tracks
5779 // i.e. either those times, or those without
5780 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5782 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5783 other_tracks = g_list_reverse(other_tracks);
5785 if ( !other_tracks ) {
5786 if ( udata.with_timestamps )
5787 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5789 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5793 // Sort alphabetically for user presentation
5794 // Convert into list of names for usage with dialog function
5795 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5796 GList *other_tracks_names = NULL;
5797 GList *iter = g_list_first ( other_tracks );
5799 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5800 iter = g_list_next ( iter );
5803 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5805 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5809 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5810 g_list_free(other_tracks);
5811 g_list_free(other_tracks_names);
5816 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5817 VikTrack *merge_track;
5818 if ( track->is_route )
5819 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5821 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5824 vik_track_steal_and_append_trackpoints ( track, merge_track );
5825 if ( track->is_route )
5826 vik_trw_layer_delete_route (vtl, merge_track);
5828 vik_trw_layer_delete_track (vtl, merge_track);
5829 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5832 for (l = merge_list; l != NULL; l = g_list_next(l))
5834 g_list_free(merge_list);
5836 vik_layer_emit_update( VIK_LAYER(vtl) );
5840 // c.f. trw_layer_sorted_track_id_by_name_list
5841 // but don't add the specified track to the list (normally current track)
5842 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5844 twt_udata *user_data = udata;
5847 if (trk == user_data->exclude) {
5851 // Sort named list alphabetically
5852 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5856 * Join - this allows combining 'tracks' and 'track routes'
5857 * i.e. doesn't care about whether tracks have consistent timestamps
5858 * ATM can only append one track at a time to the currently selected track
5860 static void trw_layer_append_track ( menu_array_sublayer values )
5863 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5865 GHashTable *ght_tracks;
5866 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5867 ght_tracks = vtl->routes;
5869 ght_tracks = vtl->tracks;
5871 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5876 GList *other_tracks_names = NULL;
5878 // Sort alphabetically for user presentation
5879 // Convert into list of names for usage with dialog function
5880 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5882 udata.result = &other_tracks_names;
5883 udata.exclude = trk;
5885 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5887 // Note the limit to selecting one track only
5888 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5889 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5890 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5893 trk->is_route ? _("Append Route"): _("Append Track"),
5894 trk->is_route ? _("Select the route to append after the current route") :
5895 _("Select the track to append after the current track") );
5897 g_list_free(other_tracks_names);
5899 // It's a list, but shouldn't contain more than one other track!
5900 if ( append_list ) {
5902 for (l = append_list; l != NULL; l = g_list_next(l)) {
5903 // TODO: at present this uses the first track found by name,
5904 // which with potential multiple same named tracks may not be the one selected...
5905 VikTrack *append_track;
5906 if ( trk->is_route )
5907 append_track = vik_trw_layer_get_route ( vtl, l->data );
5909 append_track = vik_trw_layer_get_track ( vtl, l->data );
5911 if ( append_track ) {
5912 vik_track_steal_and_append_trackpoints ( trk, append_track );
5913 if ( trk->is_route )
5914 vik_trw_layer_delete_route (vtl, append_track);
5916 vik_trw_layer_delete_track (vtl, append_track);
5919 for (l = append_list; l != NULL; l = g_list_next(l))
5921 g_list_free(append_list);
5923 vik_layer_emit_update( VIK_LAYER(vtl) );
5928 * Very similar to trw_layer_append_track for joining
5929 * but this allows selection from the 'other' list
5930 * If a track is selected, then is shows routes and joins the selected one
5931 * If a route is selected, then is shows tracks and joins the selected one
5933 static void trw_layer_append_other ( menu_array_sublayer values )
5936 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5938 GHashTable *ght_mykind, *ght_others;
5939 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5940 ght_mykind = vtl->routes;
5941 ght_others = vtl->tracks;
5944 ght_mykind = vtl->tracks;
5945 ght_others = vtl->routes;
5948 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5953 GList *other_tracks_names = NULL;
5955 // Sort alphabetically for user presentation
5956 // Convert into list of names for usage with dialog function
5957 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5959 udata.result = &other_tracks_names;
5960 udata.exclude = trk;
5962 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5964 // Note the limit to selecting one track only
5965 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5966 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5967 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5970 trk->is_route ? _("Append Track"): _("Append Route"),
5971 trk->is_route ? _("Select the track to append after the current route") :
5972 _("Select the route to append after the current track") );
5974 g_list_free(other_tracks_names);
5976 // It's a list, but shouldn't contain more than one other track!
5977 if ( append_list ) {
5979 for (l = append_list; l != NULL; l = g_list_next(l)) {
5980 // TODO: at present this uses the first track found by name,
5981 // which with potential multiple same named tracks may not be the one selected...
5983 // Get FROM THE OTHER TYPE list
5984 VikTrack *append_track;
5985 if ( trk->is_route )
5986 append_track = vik_trw_layer_get_track ( vtl, l->data );
5988 append_track = vik_trw_layer_get_route ( vtl, l->data );
5990 if ( append_track ) {
5992 if ( !append_track->is_route &&
5993 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5994 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5996 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5997 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5998 vik_track_merge_segments ( append_track );
5999 vik_track_to_routepoints ( append_track );
6006 vik_track_steal_and_append_trackpoints ( trk, append_track );
6008 // Delete copied which is FROM THE OTHER TYPE list
6009 if ( trk->is_route )
6010 vik_trw_layer_delete_track (vtl, append_track);
6012 vik_trw_layer_delete_route (vtl, append_track);
6015 for (l = append_list; l != NULL; l = g_list_next(l))
6017 g_list_free(append_list);
6018 vik_layer_emit_update( VIK_LAYER(vtl) );
6022 /* merge by segments */
6023 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6025 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6026 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6027 guint segments = vik_track_merge_segments ( trk );
6028 // NB currently no need to redraw as segments not actually shown on the display
6029 // However inform the user of what happened:
6031 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6032 g_snprintf(str, 64, tmp_str, segments);
6033 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6036 /* merge by time routine */
6037 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6039 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6043 GList *tracks_with_timestamp = NULL;
6044 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6045 if (orig_trk->trackpoints &&
6046 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6047 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6052 udata.result = &tracks_with_timestamp;
6053 udata.exclude = orig_trk;
6054 udata.with_timestamps = TRUE;
6055 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6056 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6058 if (!tracks_with_timestamp) {
6059 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6062 g_list_free(tracks_with_timestamp);
6064 static guint threshold_in_minutes = 1;
6065 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6066 _("Merge Threshold..."),
6067 _("Merge when time between tracks less than:"),
6068 &threshold_in_minutes)) {
6072 // keep attempting to merge all tracks until no merges within the time specified is possible
6073 gboolean attempt_merge = TRUE;
6074 GList *nearby_tracks = NULL;
6076 static gpointer params[3];
6078 while ( attempt_merge ) {
6080 // Don't try again unless tracks have changed
6081 attempt_merge = FALSE;
6083 trps = orig_trk->trackpoints;
6087 if (nearby_tracks) {
6088 g_list_free(nearby_tracks);
6089 nearby_tracks = NULL;
6092 params[0] = &nearby_tracks;
6093 params[1] = orig_trk;
6094 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6096 /* get a list of adjacent-in-time tracks */
6097 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6100 GList *l = nearby_tracks;
6102 /* remove trackpoints from merged track, delete track */
6103 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6104 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6106 // Tracks have changed, therefore retry again against all the remaining tracks
6107 attempt_merge = TRUE;
6112 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6115 g_list_free(nearby_tracks);
6117 vik_layer_emit_update( VIK_LAYER(vtl) );
6121 * Split a track at the currently selected trackpoint
6123 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6125 if ( !vtl->current_tpl )
6128 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6129 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6131 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6132 GList *newglist = g_list_alloc ();
6133 newglist->prev = NULL;
6134 newglist->next = vtl->current_tpl->next;
6135 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6136 tr->trackpoints = newglist;
6138 vtl->current_tpl->next->prev = newglist; /* end old track here */
6139 vtl->current_tpl->next = NULL;
6141 // Bounds of the selected track changed due to the split
6142 vik_track_calculate_bounds ( vtl->current_tp_track );
6144 vtl->current_tpl = newglist; /* change tp to first of new track. */
6145 vtl->current_tp_track = tr;
6148 vik_trw_layer_add_route ( vtl, name, tr );
6150 vik_trw_layer_add_track ( vtl, name, tr );
6152 // Bounds of the new track created by the split
6153 vik_track_calculate_bounds ( tr );
6159 // Also need id of newly created track
6162 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6164 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6166 if ( trkf && udata.uuid )
6167 vtl->current_tp_id = udata.uuid;
6169 vtl->current_tp_id = NULL;
6171 vik_layer_emit_update(VIK_LAYER(vtl));
6177 /* split by time routine */
6178 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6180 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6181 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6182 GList *trps = track->trackpoints;
6184 GList *newlists = NULL;
6185 GList *newtps = NULL;
6186 static guint thr = 1;
6193 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6194 _("Split Threshold..."),
6195 _("Split when time between trackpoints exceeds:"),
6200 /* iterate through trackpoints, and copy them into new lists without touching original list */
6201 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6205 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6207 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6210 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6211 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6212 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6214 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6219 if (ts - prev_ts > thr*60) {
6220 /* flush accumulated trackpoints into new list */
6221 newlists = g_list_append(newlists, g_list_reverse(newtps));
6225 /* accumulate trackpoint copies in newtps, in reverse order */
6226 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6228 iter = g_list_next(iter);
6231 newlists = g_list_append(newlists, g_list_reverse(newtps));
6234 /* put lists of trackpoints into tracks */
6236 // Only bother updating if the split results in new tracks
6237 if (g_list_length (newlists) > 1) {
6242 tr = vik_track_copy ( track, FALSE );
6243 tr->trackpoints = (GList *)(iter->data);
6245 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6246 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6247 g_free ( new_tr_name );
6248 vik_track_calculate_bounds ( tr );
6249 iter = g_list_next(iter);
6251 // Remove original track and then update the display
6252 vik_trw_layer_delete_track (vtl, track);
6253 vik_layer_emit_update(VIK_LAYER(vtl));
6255 g_list_free(newlists);
6259 * Split a track by the number of points as specified by the user
6261 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6263 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6265 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6266 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6268 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6273 // Check valid track
6274 GList *trps = track->trackpoints;
6278 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6279 _("Split Every Nth Point"),
6280 _("Split on every Nth point:"),
6281 250, // Default value as per typical limited track capacity of various GPS devices
6285 // Was a valid number returned?
6291 GList *newlists = NULL;
6292 GList *newtps = NULL;
6297 /* accumulate trackpoint copies in newtps, in reverse order */
6298 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6300 if (count >= points) {
6301 /* flush accumulated trackpoints into new list */
6302 newlists = g_list_append(newlists, g_list_reverse(newtps));
6306 iter = g_list_next(iter);
6309 // If there is a remaining chunk put that into the new split list
6310 // This may well be the whole track if no split points were encountered
6312 newlists = g_list_append(newlists, g_list_reverse(newtps));
6315 /* put lists of trackpoints into tracks */
6317 // Only bother updating if the split results in new tracks
6318 if (g_list_length (newlists) > 1) {
6323 tr = vik_track_copy ( track, FALSE );
6324 tr->trackpoints = (GList *)(iter->data);
6326 if ( track->is_route ) {
6327 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6328 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6331 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6332 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6334 g_free ( new_tr_name );
6335 vik_track_calculate_bounds ( tr );
6337 iter = g_list_next(iter);
6339 // Remove original track and then update the display
6340 if ( track->is_route )
6341 vik_trw_layer_delete_route (vtl, track);
6343 vik_trw_layer_delete_track (vtl, track);
6344 vik_layer_emit_update(VIK_LAYER(vtl));
6346 g_list_free(newlists);
6350 * Split a track at the currently selected trackpoint
6352 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6354 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6355 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6356 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6360 * Split a track by its segments
6361 * Routes do not have segments so don't call this for routes
6363 static void trw_layer_split_segments ( menu_array_sublayer values )
6365 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6366 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6373 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6376 for ( i = 0; i < ntracks; i++ ) {
6378 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6379 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6380 g_free ( new_tr_name );
6385 // Remove original track
6386 vik_trw_layer_delete_track ( vtl, trk );
6387 vik_layer_emit_update ( VIK_LAYER(vtl) );
6390 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6393 /* end of split/merge routines */
6395 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6399 // Find available adjacent trackpoint
6400 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6401 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6402 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6404 // Delete current trackpoint
6405 vik_trackpoint_free ( vtl->current_tpl->data );
6406 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6408 // Set to current to the available adjacent trackpoint
6409 vtl->current_tpl = new_tpl;
6411 if ( vtl->current_tp_track ) {
6412 vik_track_calculate_bounds ( vtl->current_tp_track );
6416 // Delete current trackpoint
6417 vik_trackpoint_free ( vtl->current_tpl->data );
6418 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6419 trw_layer_cancel_current_tp ( vtl, FALSE );
6424 * Delete the selected point
6426 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6428 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6430 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6431 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6433 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6438 if ( !vtl->current_tpl )
6441 trw_layer_trackpoint_selected_delete ( vtl, trk );
6443 // Track has been updated so update tps:
6444 trw_layer_cancel_tps_of_track ( vtl, trk );
6446 vik_layer_emit_update ( VIK_LAYER(vtl) );
6450 * Delete adjacent track points at the same position
6451 * AKA Delete Dulplicates on the Properties Window
6453 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6455 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6457 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6458 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6460 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6465 gulong removed = vik_track_remove_dup_points ( trk );
6467 // Track has been updated so update tps:
6468 trw_layer_cancel_tps_of_track ( vtl, trk );
6470 // Inform user how much was deleted as it's not obvious from the normal view
6472 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6473 g_snprintf(str, 64, tmp_str, removed);
6474 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6476 vik_layer_emit_update ( VIK_LAYER(vtl) );
6480 * Delete adjacent track points with the same timestamp
6481 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6483 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6485 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6487 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6488 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6490 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6495 gulong removed = vik_track_remove_same_time_points ( trk );
6497 // Track has been updated so update tps:
6498 trw_layer_cancel_tps_of_track ( vtl, trk );
6500 // Inform user how much was deleted as it's not obvious from the normal view
6502 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6503 g_snprintf(str, 64, tmp_str, removed);
6504 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6506 vik_layer_emit_update ( VIK_LAYER(vtl) );
6512 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6514 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6516 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6517 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6519 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6524 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6526 vik_layer_emit_update ( VIK_LAYER(vtl) );
6529 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6531 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6533 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6534 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6536 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6541 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6543 vik_layer_emit_update ( VIK_LAYER(vtl) );
6549 static void trw_layer_reverse ( menu_array_sublayer values )
6551 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6553 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6554 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6556 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6561 vik_track_reverse ( track );
6563 vik_layer_emit_update ( VIK_LAYER(vtl) );
6567 * Open a diary at the specified date
6569 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6572 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6573 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6574 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6575 g_error_free ( err );
6581 * Open a diary at the date of the track or waypoint
6583 static void trw_layer_diary ( menu_array_sublayer values )
6585 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6587 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6588 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6594 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6595 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6596 trw_layer_diary_open ( vtl, date_buf );
6599 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6601 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6602 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6608 if ( wpt->has_timestamp ) {
6609 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6610 trw_layer_diary_open ( vtl, date_buf );
6613 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6618 * Similar to trw_layer_enum_item, but this uses a sorted method
6621 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6623 GList **list = (GList**)udata;
6624 // *list = g_list_prepend(*all, key); //unsorted method
6625 // Sort named list alphabetically
6626 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6631 * Now Waypoint specific sort
6633 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6635 GList **list = (GList**)udata;
6636 // Sort named list alphabetically
6637 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6641 * Track specific sort
6643 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6645 GList **list = (GList**)udata;
6646 // Sort named list alphabetically
6647 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6652 gboolean has_same_track_name;
6653 const gchar *same_track_name;
6654 } same_track_name_udata;
6656 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6658 const gchar* namea = (const gchar*) aa;
6659 const gchar* nameb = (const gchar*) bb;
6662 gint result = strcmp ( namea, nameb );
6664 if ( result == 0 ) {
6665 // Found two names the same
6666 same_track_name_udata *user_data = udata;
6667 user_data->has_same_track_name = TRUE;
6668 user_data->same_track_name = namea;
6671 // Leave ordering the same
6676 * Find out if any tracks have the same name in this hash table
6678 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6680 // Sort items by name, then compare if any next to each other are the same
6682 GList *track_names = NULL;
6683 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6686 if ( ! track_names )
6689 same_track_name_udata udata;
6690 udata.has_same_track_name = FALSE;
6692 // Use sort routine to traverse list comparing items
6693 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6694 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6695 // Still no tracks...
6699 return udata.has_same_track_name;
6703 * Force unqiue track names for the track table specified
6704 * Note the panel is a required parameter to enable the update of the names displayed
6705 * Specify if on tracks or else on routes
6707 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6709 // . Search list for an instance of repeated name
6710 // . get track of this name
6711 // . create new name
6712 // . rename track & update equiv. treeview iter
6713 // . repeat until all different
6715 same_track_name_udata udata;
6717 GList *track_names = NULL;
6718 udata.has_same_track_name = FALSE;
6719 udata.same_track_name = NULL;
6721 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6724 if ( ! track_names )
6727 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6729 // Still no tracks...
6730 if ( ! dummy_list1 )
6733 while ( udata.has_same_track_name ) {
6735 // Find a track with the same name
6738 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6740 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6744 g_critical("Houston, we've had a problem.");
6745 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6746 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6751 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6752 vik_track_set_name ( trk, newname );
6758 // Need want key of it for treeview update
6759 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6761 if ( trkf && udataU.uuid ) {
6765 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6767 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6770 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6772 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6774 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6778 // Start trying to find same names again...
6780 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6781 udata.has_same_track_name = FALSE;
6782 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6784 // No tracks any more - give up searching
6785 if ( ! dummy_list2 )
6786 udata.has_same_track_name = FALSE;
6790 vik_layers_panel_emit_update ( vlp );
6793 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6795 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6798 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6799 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6800 iter = &(vtl->tracks_iter);
6801 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6803 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6804 iter = &(vtl->routes_iter);
6805 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6807 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6808 iter = &(vtl->waypoints_iter);
6809 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6813 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6816 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6818 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6821 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6822 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6823 iter = &(vtl->tracks_iter);
6824 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6826 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6827 iter = &(vtl->routes_iter);
6828 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6830 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6831 iter = &(vtl->waypoints_iter);
6832 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6836 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6842 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6844 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6847 // Ensure list of track names offered is unique
6848 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6849 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6850 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6851 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6857 // Sort list alphabetically for better presentation
6858 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6861 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6865 // Get list of items to delete from the user
6866 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6869 _("Delete Selection"),
6870 _("Select tracks to delete"));
6873 // Delete requested tracks
6874 // since specificly requested, IMHO no need for extra confirmation
6875 if ( delete_list ) {
6877 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6878 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6879 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6881 g_list_free(delete_list);
6882 vik_layer_emit_update( VIK_LAYER(vtl) );
6889 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6891 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6894 // Ensure list of track names offered is unique
6895 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6896 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6897 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6898 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6904 // Sort list alphabetically for better presentation
6905 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6908 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6912 // Get list of items to delete from the user
6913 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6916 _("Delete Selection"),
6917 _("Select routes to delete") );
6920 // Delete requested routes
6921 // since specificly requested, IMHO no need for extra confirmation
6922 if ( delete_list ) {
6924 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6925 // This deletes first route it finds of that name (but uniqueness is enforced above)
6926 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6928 g_list_free(delete_list);
6929 vik_layer_emit_update( VIK_LAYER(vtl) );
6934 gboolean has_same_waypoint_name;
6935 const gchar *same_waypoint_name;
6936 } same_waypoint_name_udata;
6938 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6940 const gchar* namea = (const gchar*) aa;
6941 const gchar* nameb = (const gchar*) bb;
6944 gint result = strcmp ( namea, nameb );
6946 if ( result == 0 ) {
6947 // Found two names the same
6948 same_waypoint_name_udata *user_data = udata;
6949 user_data->has_same_waypoint_name = TRUE;
6950 user_data->same_waypoint_name = namea;
6953 // Leave ordering the same
6958 * Find out if any waypoints have the same name in this layer
6960 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6962 // Sort items by name, then compare if any next to each other are the same
6964 GList *waypoint_names = NULL;
6965 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6968 if ( ! waypoint_names )
6971 same_waypoint_name_udata udata;
6972 udata.has_same_waypoint_name = FALSE;
6974 // Use sort routine to traverse list comparing items
6975 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6976 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6977 // Still no waypoints...
6981 return udata.has_same_waypoint_name;
6985 * Force unqiue waypoint names for this layer
6986 * Note the panel is a required parameter to enable the update of the names displayed
6988 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6990 // . Search list for an instance of repeated name
6991 // . get waypoint of this name
6992 // . create new name
6993 // . rename waypoint & update equiv. treeview iter
6994 // . repeat until all different
6996 same_waypoint_name_udata udata;
6998 GList *waypoint_names = NULL;
6999 udata.has_same_waypoint_name = FALSE;
7000 udata.same_waypoint_name = NULL;
7002 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7005 if ( ! waypoint_names )
7008 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7010 // Still no waypoints...
7011 if ( ! dummy_list1 )
7014 while ( udata.has_same_waypoint_name ) {
7016 // Find a waypoint with the same name
7017 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7021 g_critical("Houston, we've had a problem.");
7022 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7023 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7028 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7030 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7032 // Start trying to find same names again...
7033 waypoint_names = NULL;
7034 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7035 udata.has_same_waypoint_name = FALSE;
7036 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7038 // No waypoints any more - give up searching
7039 if ( ! dummy_list2 )
7040 udata.has_same_waypoint_name = FALSE;
7044 vik_layers_panel_emit_update ( vlp );
7050 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7052 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7055 // Ensure list of waypoint names offered is unique
7056 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7057 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7058 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7059 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7065 // Sort list alphabetically for better presentation
7066 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7068 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7072 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7074 // Get list of items to delete from the user
7075 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7078 _("Delete Selection"),
7079 _("Select waypoints to delete"));
7082 // Delete requested waypoints
7083 // since specificly requested, IMHO no need for extra confirmation
7084 if ( delete_list ) {
7086 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7087 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7088 trw_layer_delete_waypoint_by_name (vtl, l->data);
7090 g_list_free(delete_list);
7092 trw_layer_calculate_bounds_waypoints ( vtl );
7093 vik_layer_emit_update( VIK_LAYER(vtl) );
7101 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7103 vik_treeview_item_toggle_visible ( vt, it );
7109 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7111 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7117 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7119 wp->visible = GPOINTER_TO_INT (on_off);
7125 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7127 wp->visible = !wp->visible;
7133 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7135 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7136 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7137 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7138 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7140 vik_layer_emit_update ( VIK_LAYER(vtl) );
7146 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7148 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7149 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7150 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7151 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7153 vik_layer_emit_update ( VIK_LAYER(vtl) );
7159 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7161 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7162 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7163 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7165 vik_layer_emit_update ( VIK_LAYER(vtl) );
7171 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7173 trk->visible = GPOINTER_TO_INT (on_off);
7179 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7181 trk->visible = !trk->visible;
7187 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7189 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7190 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7191 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7192 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7194 vik_layer_emit_update ( VIK_LAYER(vtl) );
7200 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7202 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7203 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7204 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7205 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7207 vik_layer_emit_update ( VIK_LAYER(vtl) );
7213 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7215 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7216 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7217 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7219 vik_layer_emit_update ( VIK_LAYER(vtl) );
7225 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7227 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7228 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7229 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7230 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7232 vik_layer_emit_update ( VIK_LAYER(vtl) );
7238 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7240 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7241 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7242 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7243 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7245 vik_layer_emit_update ( VIK_LAYER(vtl) );
7251 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7253 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7254 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7255 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7257 vik_layer_emit_update ( VIK_LAYER(vtl) );
7261 * vik_trw_layer_build_waypoint_list_t:
7263 * Helper function to construct a list of #vik_trw_waypoint_list_t
7265 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7267 GList *waypoints_and_layers = NULL;
7268 // build waypoints_and_layers list
7269 while ( waypoints ) {
7270 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7271 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7273 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7274 waypoints = g_list_next ( waypoints );
7276 return waypoints_and_layers;
7280 * trw_layer_create_waypoint_list:
7282 * Create the latest list of waypoints with the associated layer(s)
7283 * Although this will always be from a single layer here
7285 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7287 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7288 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7290 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7294 * trw_layer_analyse_close:
7296 * Stuff to do on dialog closure
7298 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7300 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7301 gtk_widget_destroy ( dialog );
7302 vtl->tracks_analysis_dialog = NULL;
7306 * vik_trw_layer_build_track_list_t:
7308 * Helper function to construct a list of #vik_trw_track_list_t
7310 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7312 GList *tracks_and_layers = NULL;
7313 // build tracks_and_layers list
7315 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7316 vtdl->trk = VIK_TRACK(tracks->data);
7318 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7319 tracks = g_list_next ( tracks );
7321 return tracks_and_layers;
7325 * trw_layer_create_track_list:
7327 * Create the latest list of tracks with the associated layer(s)
7328 * Although this will always be from a single layer here
7330 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7332 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7333 GList *tracks = NULL;
7334 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7335 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7337 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7339 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7342 static void trw_layer_tracks_stats ( menu_array_layer values )
7344 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7345 // There can only be one!
7346 if ( vtl->tracks_analysis_dialog )
7349 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7350 VIK_LAYER(vtl)->name,
7352 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7353 trw_layer_create_track_list,
7354 trw_layer_analyse_close );
7360 static void trw_layer_routes_stats ( menu_array_layer values )
7362 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7363 // There can only be one!
7364 if ( vtl->tracks_analysis_dialog )
7367 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7368 VIK_LAYER(vtl)->name,
7370 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7371 trw_layer_create_track_list,
7372 trw_layer_analyse_close );
7375 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7377 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7378 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7380 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7383 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7385 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7386 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7389 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7390 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7394 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7396 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7397 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7401 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7402 } else if ( !strncmp(wp->comment, "http", 4) ) {
7403 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7404 } else if ( !strncmp(wp->description, "http", 4) ) {
7405 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7409 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7411 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7413 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7415 // No actual change to the name supplied
7417 if (strcmp(newname, wp->name) == 0 )
7420 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7423 // An existing waypoint has been found with the requested name
7424 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7425 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7430 // Update WP name and refresh the treeview
7431 vik_waypoint_set_name (wp, newname);
7433 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7434 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7436 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7441 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7443 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7445 // No actual change to the name supplied
7447 if (strcmp(newname, trk->name) == 0)
7450 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7453 // An existing track has been found with the requested name
7454 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7455 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7459 // Update track name and refresh GUI parts
7460 vik_track_set_name (trk, newname);
7462 // Update any subwindows that could be displaying this track which has changed name
7463 // Only one Track Edit Window
7464 if ( l->current_tp_track == trk && l->tpwin ) {
7465 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7467 // Property Dialog of the track
7468 vik_trw_layer_propwin_update ( trk );
7470 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7471 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7473 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7478 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7480 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7482 // No actual change to the name supplied
7484 if (strcmp(newname, trk->name) == 0)
7487 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7490 // An existing track has been found with the requested name
7491 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7492 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7496 // Update track name and refresh GUI parts
7497 vik_track_set_name (trk, newname);
7499 // Update any subwindows that could be displaying this track which has changed name
7500 // Only one Track Edit Window
7501 if ( l->current_tp_track == trk && l->tpwin ) {
7502 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7504 // Property Dialog of the track
7505 vik_trw_layer_propwin_update ( trk );
7507 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7508 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7510 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7517 static gboolean is_valid_geocache_name ( gchar *str )
7519 gint len = strlen ( str );
7520 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]));
7523 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7525 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7526 a_acquire_set_filter_track ( trk );
7529 #ifdef VIK_CONFIG_GOOGLE
7530 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7532 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7533 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7536 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7538 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7540 gchar *escaped = uri_escape ( tr->comment );
7541 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7542 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7549 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7550 /* viewpoint is now available instead */
7551 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7553 static menu_array_sublayer pass_along;
7555 gboolean rv = FALSE;
7557 pass_along[MA_VTL] = l;
7558 pass_along[MA_VLP] = vlp;
7559 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7560 pass_along[MA_SUBLAYER_ID] = sublayer;
7561 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7562 pass_along[MA_VVP] = vvp;
7563 pass_along[MA_TV_ITER] = iter;
7564 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7566 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7570 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7573 gtk_widget_show ( item );
7575 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7576 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7577 if (tr && tr->property_dialog)
7578 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7580 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7581 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7582 if (tr && tr->property_dialog)
7583 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7586 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7588 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7589 gtk_widget_show ( item );
7591 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7592 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7593 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7594 gtk_widget_show ( item );
7596 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7597 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7598 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7599 gtk_widget_show ( item );
7601 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7603 // Always create separator as now there is always at least the transform menu option
7604 item = gtk_menu_item_new ();
7605 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7606 gtk_widget_show ( item );
7608 /* could be a right-click using the tool */
7609 if ( vlp != NULL ) {
7610 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7613 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7614 gtk_widget_show ( item );
7617 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7619 if ( wp && wp->name ) {
7620 if ( is_valid_geocache_name ( wp->name ) ) {
7621 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7623 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7624 gtk_widget_show ( item );
7626 #ifdef VIK_CONFIG_GEOTAG
7627 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7629 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7630 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7631 gtk_widget_show ( item );
7635 if ( wp && wp->image )
7637 // Set up image paramater
7638 pass_along[MA_MISC] = wp->image;
7640 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7641 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
7642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7643 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7644 gtk_widget_show ( item );
7646 #ifdef VIK_CONFIG_GEOTAG
7647 GtkWidget *geotag_submenu = gtk_menu_new ();
7648 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7650 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7651 gtk_widget_show ( item );
7652 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7654 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7655 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7656 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7657 gtk_widget_show ( item );
7659 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7661 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7662 gtk_widget_show ( item );
7669 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7670 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7671 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7672 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7673 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7674 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7675 gtk_widget_show ( item );
7681 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7682 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7683 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7684 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7685 gtk_widget_show ( item );
7686 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7687 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7688 gtk_widget_set_sensitive ( item, TRUE );
7690 gtk_widget_set_sensitive ( item, FALSE );
7693 item = gtk_menu_item_new ();
7694 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7695 gtk_widget_show ( item );
7698 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7701 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7702 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7703 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7704 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7705 gtk_widget_show ( item );
7708 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7710 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7711 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7713 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7714 gtk_widget_show ( item );
7716 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7717 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7718 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720 gtk_widget_show ( item );
7722 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7723 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7724 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7725 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7726 gtk_widget_show ( item );
7728 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7729 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7730 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7731 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7732 gtk_widget_show ( item );
7734 GtkWidget *vis_submenu = gtk_menu_new ();
7735 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7736 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7737 gtk_widget_show ( item );
7738 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7740 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7741 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7743 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7744 gtk_widget_show ( item );
7746 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7747 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7748 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7749 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7750 gtk_widget_show ( item );
7752 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7753 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7754 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7755 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7756 gtk_widget_show ( item );
7758 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7760 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7761 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7764 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7768 if ( l->current_track && !l->current_track->is_route ) {
7769 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7771 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7772 gtk_widget_show ( item );
7774 item = gtk_menu_item_new ();
7775 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7776 gtk_widget_show ( item );
7779 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7780 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7782 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7783 gtk_widget_show ( item );
7785 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7788 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7789 gtk_widget_show ( item );
7790 // Make it available only when a new track *not* already in progress
7791 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7793 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7794 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7795 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7796 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7797 gtk_widget_show ( item );
7799 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7800 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7801 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7802 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7803 gtk_widget_show ( item );
7805 GtkWidget *vis_submenu = gtk_menu_new ();
7806 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7807 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7808 gtk_widget_show ( item );
7809 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7811 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7812 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7814 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7815 gtk_widget_show ( item );
7817 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7818 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7819 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7820 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7821 gtk_widget_show ( item );
7823 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7824 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7825 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7826 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7828 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7829 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7831 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7832 gtk_widget_show ( item );
7834 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7836 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7837 gtk_widget_show ( item );
7840 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7844 if ( l->current_track && l->current_track->is_route ) {
7845 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7846 // Reuse finish track method
7847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7848 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7849 gtk_widget_show ( item );
7851 item = gtk_menu_item_new ();
7852 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7853 gtk_widget_show ( item );
7856 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7857 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7859 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7860 gtk_widget_show ( item );
7862 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7863 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7865 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7866 gtk_widget_show ( item );
7867 // Make it available only when a new track *not* already in progress
7868 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7870 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7871 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7873 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7874 gtk_widget_show ( item );
7876 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7877 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7878 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7879 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7880 gtk_widget_show ( item );
7882 GtkWidget *vis_submenu = gtk_menu_new ();
7883 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7884 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7885 gtk_widget_show ( item );
7886 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7888 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7889 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7890 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7891 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7892 gtk_widget_show ( item );
7894 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7895 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7897 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7898 gtk_widget_show ( item );
7900 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7901 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7905 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7908 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7910 gtk_widget_show ( item );
7912 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7913 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7914 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7915 gtk_widget_show ( item );
7919 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7920 GtkWidget *submenu_sort = gtk_menu_new ();
7921 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7922 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7923 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7924 gtk_widget_show ( item );
7925 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7927 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7928 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7930 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7931 gtk_widget_show ( item );
7933 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7934 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7936 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7937 gtk_widget_show ( item );
7940 GtkWidget *upload_submenu = gtk_menu_new ();
7942 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7944 item = gtk_menu_item_new ();
7945 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7946 gtk_widget_show ( item );
7948 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7949 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7950 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7951 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7952 if ( l->current_track ) {
7953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7954 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7955 gtk_widget_show ( item );
7958 item = gtk_menu_item_new ();
7959 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7960 gtk_widget_show ( item );
7963 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7964 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7966 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7967 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7969 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7970 gtk_widget_show ( item );
7972 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7974 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7975 gtk_widget_show ( item );
7977 GtkWidget *goto_submenu;
7978 goto_submenu = gtk_menu_new ();
7979 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7980 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7981 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7982 gtk_widget_show ( item );
7983 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7985 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7988 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7989 gtk_widget_show ( item );
7991 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7992 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7994 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7995 gtk_widget_show ( item );
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7998 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8001 gtk_widget_show ( item );
8003 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8004 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8006 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8007 gtk_widget_show ( item );
8009 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8010 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8011 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8012 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8013 gtk_widget_show ( item );
8015 // Routes don't have speeds
8016 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8017 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8018 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8020 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8021 gtk_widget_show ( item );
8024 GtkWidget *combine_submenu;
8025 combine_submenu = gtk_menu_new ();
8026 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8028 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8029 gtk_widget_show ( item );
8030 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8032 // Routes don't have times or segments...
8033 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8034 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8036 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8037 gtk_widget_show ( item );
8039 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8041 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8042 gtk_widget_show ( item );
8045 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8046 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8047 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8048 gtk_widget_show ( item );
8050 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8051 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8053 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8056 gtk_widget_show ( item );
8058 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8059 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8061 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8063 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8064 gtk_widget_show ( item );
8066 GtkWidget *split_submenu;
8067 split_submenu = gtk_menu_new ();
8068 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8070 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8071 gtk_widget_show ( item );
8072 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8074 // Routes don't have times or segments...
8075 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8076 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8078 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8079 gtk_widget_show ( item );
8081 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8082 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8084 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8085 gtk_widget_show ( item );
8088 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8090 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8091 gtk_widget_show ( item );
8093 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8095 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8096 gtk_widget_show ( item );
8097 // Make it available only when a trackpoint is selected.
8098 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8100 GtkWidget *insert_submenu = gtk_menu_new ();
8101 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8103 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8104 gtk_widget_show ( item );
8105 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8107 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8109 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8110 gtk_widget_show ( item );
8111 // Make it available only when a point is selected
8112 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8114 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8116 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8117 gtk_widget_show ( item );
8118 // Make it available only when a point is selected
8119 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8121 GtkWidget *delete_submenu;
8122 delete_submenu = gtk_menu_new ();
8123 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8124 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8125 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8126 gtk_widget_show ( item );
8127 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8129 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8132 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8133 gtk_widget_show ( item );
8134 // Make it available only when a point is selected
8135 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8137 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8139 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8140 gtk_widget_show ( item );
8142 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8144 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8145 gtk_widget_show ( item );
8147 GtkWidget *transform_submenu;
8148 transform_submenu = gtk_menu_new ();
8149 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8150 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8151 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8152 gtk_widget_show ( item );
8153 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8155 GtkWidget *dem_submenu;
8156 dem_submenu = gtk_menu_new ();
8157 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8158 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
8159 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8160 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8162 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8164 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8165 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8166 gtk_widget_show ( item );
8168 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8169 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8170 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8171 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8172 gtk_widget_show ( item );
8174 GtkWidget *smooth_submenu;
8175 smooth_submenu = gtk_menu_new ();
8176 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8177 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8178 gtk_widget_show ( item );
8179 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8181 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8183 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8184 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8185 gtk_widget_show ( item );
8187 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8189 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8190 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8191 gtk_widget_show ( item );
8193 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8194 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8196 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8197 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8199 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8200 gtk_widget_show ( item );
8202 // Routes don't have timestamps - so this is only available for tracks
8203 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8204 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8205 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8206 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8207 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8208 gtk_widget_show ( item );
8211 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8212 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8214 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8215 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8216 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8217 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8218 gtk_widget_show ( item );
8220 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8221 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8222 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8223 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8224 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8225 gtk_widget_show ( item );
8228 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8230 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8231 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8233 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8234 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
8235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8236 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8237 gtk_widget_show ( item );
8240 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8241 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8243 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8244 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8245 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8246 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8247 gtk_widget_show ( item );
8249 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8250 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8252 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8253 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8255 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8256 gtk_widget_show ( item );
8258 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8259 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8260 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
8261 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8262 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8263 gtk_widget_show ( item );
8266 // ATM can't upload a single waypoint but can do waypoints to a GPS
8267 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8268 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8269 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8270 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8271 gtk_widget_show ( item );
8272 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8274 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8275 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8277 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8278 gtk_widget_show ( item );
8282 // Only made available if a suitable program is installed
8283 if ( have_diary_program ) {
8284 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8285 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8286 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8288 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8289 gtk_widget_show ( item );
8293 #ifdef VIK_CONFIG_GOOGLE
8294 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8296 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8299 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8300 gtk_widget_show ( item );
8304 // Some things aren't usable with routes
8305 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8306 #ifdef VIK_CONFIG_OPENSTREETMAP
8307 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8308 // Convert internal pointer into track
8309 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8310 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8311 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8312 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8313 gtk_widget_show ( item );
8316 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8317 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8318 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8319 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8320 gtk_widget_show ( item );
8322 /* ATM This function is only available via the layers panel, due to needing a vlp */
8324 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8325 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8326 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8329 gtk_widget_show ( item );
8333 #ifdef VIK_CONFIG_GEOTAG
8334 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8335 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8336 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8337 gtk_widget_show ( item );
8341 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8342 // Only show on viewport popmenu when a trackpoint is selected
8343 if ( ! vlp && l->current_tpl ) {
8345 item = gtk_menu_item_new ();
8346 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8347 gtk_widget_show ( item );
8349 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8350 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8352 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8353 gtk_widget_show ( item );
8357 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8358 GtkWidget *transform_submenu;
8359 transform_submenu = gtk_menu_new ();
8360 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8361 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8362 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8363 gtk_widget_show ( item );
8364 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8366 GtkWidget *dem_submenu;
8367 dem_submenu = gtk_menu_new ();
8368 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8369 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
8370 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8371 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8373 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8374 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8375 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8376 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8377 gtk_widget_show ( item );
8379 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8381 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8382 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8383 gtk_widget_show ( item );
8386 gtk_widget_show_all ( GTK_WIDGET(menu) );
8391 // TODO: Probably better to rework this track manipulation in viktrack.c
8392 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8395 if (!vtl->current_tpl)
8398 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8399 VikTrackpoint *tp_other = NULL;
8402 if (!vtl->current_tpl->prev)
8404 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8406 if (!vtl->current_tpl->next)
8408 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8411 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8414 VikTrackpoint *tp_new = vik_trackpoint_new();
8415 struct LatLon ll_current, ll_other;
8416 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8417 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8419 /* main positional interpolation */
8420 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8421 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8423 /* Now other properties that can be interpolated */
8424 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8426 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8427 /* Note here the division is applied to each part, then added
8428 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8429 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8430 tp_new->has_timestamp = TRUE;
8433 if (tp_current->speed != NAN && tp_other->speed != NAN)
8434 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8436 /* TODO - improve interpolation of course, as it may not be correct.
8437 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8438 [similar applies if value is in radians] */
8439 if (tp_current->course != NAN && tp_other->course != NAN)
8440 tp_new->course = (tp_current->course + tp_other->course)/2;
8442 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8444 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8445 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8447 // Otherwise try routes
8448 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8452 gint index = g_list_index ( trk->trackpoints, tp_current );
8456 // NB no recalculation of bounds since it is inserted between points
8457 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8462 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8468 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8472 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8474 if ( vtl->current_tpl )
8476 vtl->current_tpl = NULL;
8477 vtl->current_tp_track = NULL;
8478 vtl->current_tp_id = NULL;
8479 vik_layer_emit_update(VIK_LAYER(vtl));
8483 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8485 g_assert ( vtl->tpwin != NULL );
8486 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8487 trw_layer_cancel_current_tp ( vtl, TRUE );
8489 if ( vtl->current_tpl == NULL )
8492 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8494 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8495 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8497 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8499 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8501 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8505 trw_layer_trackpoint_selected_delete ( vtl, tr );
8507 if ( vtl->current_tpl )
8508 // Reset dialog with the available adjacent trackpoint
8509 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8511 vik_layer_emit_update(VIK_LAYER(vtl));
8513 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8515 if ( vtl->current_tp_track )
8516 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8517 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8519 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8521 if ( vtl->current_tp_track )
8522 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8523 vik_layer_emit_update(VIK_LAYER(vtl));
8525 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8527 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8528 vik_layer_emit_update(VIK_LAYER(vtl));
8530 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8531 vik_layer_emit_update(VIK_LAYER(vtl));
8535 * trw_layer_dialog_shift:
8536 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8538 * Try to reposition a dialog if it's over the specified coord
8539 * so to not obscure the item of interest
8541 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8543 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8545 // Attempt force dialog to be shown so we can find out where it is more reliably...
8546 while ( gtk_events_pending() )
8547 gtk_main_iteration ();
8549 // get parent window position & size
8550 gint win_pos_x, win_pos_y;
8551 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8553 gint win_size_x, win_size_y;
8554 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8556 // get own dialog size
8557 gint dia_size_x, dia_size_y;
8558 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8560 // get own dialog position
8561 gint dia_pos_x, dia_pos_y;
8562 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8564 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8565 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8567 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8569 gint vp_xx, vp_yy; // In viewport pixels
8570 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8572 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8576 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8578 // Transform Viewport pixels into absolute pixels
8579 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8580 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8582 // Is dialog over the point (to within an ^^ edge value)
8583 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8584 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8588 gint hh = vik_viewport_get_height ( vvp );
8590 // Consider the difference in viewport to the full window
8591 gint offset_y = dest_y;
8592 // Add difference between dialog and window sizes
8593 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8595 if ( vp_yy > hh/2 ) {
8596 // Point in bottom half, move window to top half
8597 gtk_window_move ( dialog, dia_pos_x, offset_y );
8600 // Point in top half, move dialog down
8601 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8605 // Shift left<->right
8606 gint ww = vik_viewport_get_width ( vvp );
8608 // Consider the difference in viewport to the full window
8609 gint offset_x = dest_x;
8610 // Add difference between dialog and window sizes
8611 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8613 if ( vp_xx > ww/2 ) {
8614 // Point on right, move window to left
8615 gtk_window_move ( dialog, offset_x, dia_pos_y );
8618 // Point on left, move right
8619 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8627 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8631 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8632 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8633 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8634 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8636 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8638 if ( vtl->current_tpl ) {
8639 // get tp pixel position
8640 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8642 // Shift up<->down to try not to obscure the trackpoint.
8643 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8647 if ( vtl->current_tpl )
8648 if ( vtl->current_tp_track )
8649 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8650 /* set layer name and TP data */
8653 /***************************************************************************
8655 ***************************************************************************/
8657 /*** Utility data structures and functions ****/
8661 gint closest_x, closest_y;
8662 gboolean draw_images;
8663 gpointer *closest_wp_id;
8664 VikWaypoint *closest_wp;
8670 gint closest_x, closest_y;
8671 gpointer closest_track_id;
8672 VikTrackpoint *closest_tp;
8678 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8684 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8686 // If waypoint has an image then use the image size to select
8687 if ( params->draw_images && wp->image ) {
8688 gint slackx, slacky;
8689 slackx = wp->image_width / 2;
8690 slacky = wp->image_height / 2;
8692 if ( x <= params->x + slackx && x >= params->x - slackx
8693 && y <= params->y + slacky && y >= params->y - slacky ) {
8694 params->closest_wp_id = id;
8695 params->closest_wp = wp;
8696 params->closest_x = x;
8697 params->closest_y = y;
8700 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8701 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8702 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8704 params->closest_wp_id = id;
8705 params->closest_wp = wp;
8706 params->closest_x = x;
8707 params->closest_y = y;
8711 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8713 GList *tpl = t->trackpoints;
8719 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8725 tp = VIK_TRACKPOINT(tpl->data);
8727 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8729 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8730 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8731 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8733 params->closest_track_id = id;
8734 params->closest_tp = tp;
8735 params->closest_tpl = tpl;
8736 params->closest_x = x;
8737 params->closest_y = y;
8743 // ATM: Leave this as 'Track' only.
8744 // Not overly bothered about having a snap to route trackpoint capability
8745 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8747 TPSearchParams params;
8751 params.closest_track_id = NULL;
8752 params.closest_tp = NULL;
8753 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8754 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8755 return params.closest_tp;
8758 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8760 WPSearchParams params;
8764 params.draw_images = vtl->drawimages;
8765 params.closest_wp = NULL;
8766 params.closest_wp_id = NULL;
8767 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8768 return params.closest_wp;
8772 // Some forward declarations
8773 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8774 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8775 static void marker_end_move ( tool_ed_t *t );
8778 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8782 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8784 // Here always allow snapping back to the original location
8785 // this is useful when one decides not to move the thing afterall
8786 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8789 if ( event->state & GDK_CONTROL_MASK )
8791 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8793 new_coord = tp->coord;
8797 if ( event->state & GDK_SHIFT_MASK )
8799 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8801 new_coord = wp->coord;
8805 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8807 marker_moveto ( t, x, y );
8814 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8816 if ( t->holding && event->button == 1 )
8819 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8822 if ( event->state & GDK_CONTROL_MASK )
8824 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8826 new_coord = tp->coord;
8830 if ( event->state & GDK_SHIFT_MASK )
8832 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8834 new_coord = wp->coord;
8837 marker_end_move ( t );
8839 // Determine if working on a waypoint or a trackpoint
8840 if ( t->is_waypoint ) {
8841 // Update waypoint position
8842 vtl->current_wp->coord = new_coord;
8843 trw_layer_calculate_bounds_waypoints ( vtl );
8844 // Reset waypoint pointer
8845 vtl->current_wp = NULL;
8846 vtl->current_wp_id = NULL;
8849 if ( vtl->current_tpl ) {
8850 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8852 if ( vtl->current_tp_track )
8853 vik_track_calculate_bounds ( vtl->current_tp_track );
8856 if ( vtl->current_tp_track )
8857 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8858 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8862 vik_layer_emit_update ( VIK_LAYER(vtl) );
8869 Returns true if a waypoint or track is found near the requested event position for this particular layer
8870 The item found is automatically selected
8871 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8873 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8875 if ( event->button != 1 )
8878 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8881 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8885 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8887 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8889 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8890 WPSearchParams wp_params;
8891 wp_params.vvp = vvp;
8892 wp_params.x = event->x;
8893 wp_params.y = event->y;
8894 wp_params.draw_images = vtl->drawimages;
8895 wp_params.closest_wp_id = NULL;
8896 wp_params.closest_wp = NULL;
8898 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8900 if ( wp_params.closest_wp ) {
8903 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8905 // Too easy to move it so must be holding shift to start immediately moving it
8906 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8907 if ( event->state & GDK_SHIFT_MASK ||
8908 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8909 // Put into 'move buffer'
8910 // NB vvp & vw already set in tet
8911 tet->vtl = (gpointer)vtl;
8912 tet->is_waypoint = TRUE;
8914 marker_begin_move (tet, event->x, event->y);
8917 vtl->current_wp = wp_params.closest_wp;
8918 vtl->current_wp_id = wp_params.closest_wp_id;
8920 if ( event->type == GDK_2BUTTON_PRESS ) {
8921 if ( vtl->current_wp->image ) {
8922 menu_array_sublayer values;
8923 values[MA_VTL] = vtl;
8924 values[MA_MISC] = vtl->current_wp->image;
8925 trw_layer_show_picture ( values );
8929 vik_layer_emit_update ( VIK_LAYER(vtl) );
8935 // Used for both track and route lists
8936 TPSearchParams tp_params;
8937 tp_params.vvp = vvp;
8938 tp_params.x = event->x;
8939 tp_params.y = event->y;
8940 tp_params.closest_track_id = NULL;
8941 tp_params.closest_tp = NULL;
8942 tp_params.closest_tpl = NULL;
8943 tp_params.bbox = bbox;
8945 if (vtl->tracks_visible) {
8946 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8948 if ( tp_params.closest_tp ) {
8950 // Always select + highlight the track
8951 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8953 tet->is_waypoint = FALSE;
8955 // Select the Trackpoint
8956 // Can move it immediately when control held or it's the previously selected tp
8957 if ( event->state & GDK_CONTROL_MASK ||
8958 vtl->current_tpl == tp_params.closest_tpl ) {
8959 // Put into 'move buffer'
8960 // NB vvp & vw already set in tet
8961 tet->vtl = (gpointer)vtl;
8962 marker_begin_move (tet, event->x, event->y);
8965 vtl->current_tpl = tp_params.closest_tpl;
8966 vtl->current_tp_id = tp_params.closest_track_id;
8967 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8969 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8972 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8974 vik_layer_emit_update ( VIK_LAYER(vtl) );
8979 // Try again for routes
8980 if (vtl->routes_visible) {
8981 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8983 if ( tp_params.closest_tp ) {
8985 // Always select + highlight the track
8986 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8988 tet->is_waypoint = FALSE;
8990 // Select the Trackpoint
8991 // Can move it immediately when control held or it's the previously selected tp
8992 if ( event->state & GDK_CONTROL_MASK ||
8993 vtl->current_tpl == tp_params.closest_tpl ) {
8994 // Put into 'move buffer'
8995 // NB vvp & vw already set in tet
8996 tet->vtl = (gpointer)vtl;
8997 marker_begin_move (tet, event->x, event->y);
9000 vtl->current_tpl = tp_params.closest_tpl;
9001 vtl->current_tp_id = tp_params.closest_track_id;
9002 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9004 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9007 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9009 vik_layer_emit_update ( VIK_LAYER(vtl) );
9014 /* these aren't the droids you're looking for */
9015 vtl->current_wp = NULL;
9016 vtl->current_wp_id = NULL;
9017 trw_layer_cancel_current_tp ( vtl, FALSE );
9020 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9025 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9027 if ( event->button != 3 )
9030 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9033 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9036 /* Post menu for the currently selected item */
9038 /* See if a track is selected */
9039 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9040 if ( track && track->visible ) {
9042 if ( track->name ) {
9044 if ( vtl->track_right_click_menu )
9045 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9047 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9054 if ( track->is_route )
9055 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9057 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9059 if ( trkf && udataU.uuid ) {
9062 if ( track->is_route )
9063 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9065 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9067 trw_layer_sublayer_add_menu_items ( vtl,
9068 vtl->track_right_click_menu,
9070 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9076 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9082 /* See if a waypoint is selected */
9083 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9084 if ( waypoint && waypoint->visible ) {
9085 if ( waypoint->name ) {
9087 if ( vtl->wp_right_click_menu )
9088 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9090 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9093 udata.wp = waypoint;
9096 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9098 if ( wpf && udata.uuid ) {
9099 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9101 trw_layer_sublayer_add_menu_items ( vtl,
9102 vtl->wp_right_click_menu,
9104 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9109 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9118 /* background drawing hook, to be passed the viewport */
9119 static gboolean tool_sync_done = TRUE;
9121 static gboolean tool_sync(gpointer data)
9123 VikViewport *vvp = data;
9124 gdk_threads_enter();
9125 vik_viewport_sync(vvp);
9126 tool_sync_done = TRUE;
9127 gdk_threads_leave();
9131 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9134 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9135 gdk_gc_set_function ( t->gc, GDK_INVERT );
9136 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9137 vik_viewport_sync(t->vvp);
9142 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9144 VikViewport *vvp = t->vvp;
9145 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9146 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9150 if (tool_sync_done) {
9151 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9152 tool_sync_done = FALSE;
9156 static void marker_end_move ( tool_ed_t *t )
9158 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9159 g_object_unref ( t->gc );
9163 /*** Edit waypoint ****/
9165 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9167 tool_ed_t *t = g_new(tool_ed_t, 1);
9173 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9178 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9180 WPSearchParams params;
9181 tool_ed_t *t = data;
9182 VikViewport *vvp = t->vvp;
9184 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9191 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9194 if ( vtl->current_wp && vtl->current_wp->visible )
9196 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9198 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9200 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9201 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9203 if ( event->button == 3 )
9204 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9206 marker_begin_move(t, event->x, event->y);
9213 params.x = event->x;
9214 params.y = event->y;
9215 params.draw_images = vtl->drawimages;
9216 params.closest_wp_id = NULL;
9217 params.closest_wp = NULL;
9218 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9219 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9221 if ( event->button == 3 )
9222 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9224 marker_begin_move(t, event->x, event->y);
9227 else if ( params.closest_wp )
9229 if ( event->button == 3 )
9230 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9232 vtl->waypoint_rightclick = FALSE;
9234 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9236 vtl->current_wp = params.closest_wp;
9237 vtl->current_wp_id = params.closest_wp_id;
9239 /* could make it so don't update if old WP is off screen and new is null but oh well */
9240 vik_layer_emit_update ( VIK_LAYER(vtl) );
9244 vtl->current_wp = NULL;
9245 vtl->current_wp_id = NULL;
9246 vtl->waypoint_rightclick = FALSE;
9247 vik_layer_emit_update ( VIK_LAYER(vtl) );
9251 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9253 tool_ed_t *t = data;
9254 VikViewport *vvp = t->vvp;
9256 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9261 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9264 if ( event->state & GDK_CONTROL_MASK )
9266 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9268 new_coord = tp->coord;
9272 if ( event->state & GDK_SHIFT_MASK )
9274 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9275 if ( wp && wp != vtl->current_wp )
9276 new_coord = wp->coord;
9281 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9283 marker_moveto ( t, x, y );
9290 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9292 tool_ed_t *t = data;
9293 VikViewport *vvp = t->vvp;
9295 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9298 if ( t->holding && event->button == 1 )
9301 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9304 if ( event->state & GDK_CONTROL_MASK )
9306 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9308 new_coord = tp->coord;
9312 if ( event->state & GDK_SHIFT_MASK )
9314 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9315 if ( wp && wp != vtl->current_wp )
9316 new_coord = wp->coord;
9319 marker_end_move ( t );
9321 vtl->current_wp->coord = new_coord;
9323 trw_layer_calculate_bounds_waypoints ( vtl );
9324 vik_layer_emit_update ( VIK_LAYER(vtl) );
9327 /* PUT IN RIGHT PLACE!!! */
9328 if ( event->button == 3 && vtl->waypoint_rightclick )
9330 if ( vtl->wp_right_click_menu )
9331 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9332 if ( vtl->current_wp ) {
9333 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9334 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 );
9335 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9337 vtl->waypoint_rightclick = FALSE;
9342 /*** New track ****/
9344 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9351 GdkDrawable *drawable;
9357 * Draw specified pixmap
9359 static gboolean draw_sync ( gpointer data )
9361 draw_sync_t *ds = (draw_sync_t*) data;
9362 // Sometimes don't want to draw
9363 // normally because another update has taken precedent such as panning the display
9364 // which means this pixmap is no longer valid
9365 if ( ds->vtl->draw_sync_do ) {
9366 gdk_threads_enter();
9367 gdk_draw_drawable (ds->drawable,
9370 0, 0, 0, 0, -1, -1);
9371 ds->vtl->draw_sync_done = TRUE;
9372 gdk_threads_leave();
9378 static gchar* distance_string (gdouble distance)
9382 /* draw label with distance */
9383 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9384 switch (dist_units) {
9385 case VIK_UNITS_DISTANCE_MILES:
9386 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9387 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9388 } else if (distance < 1609.4) {
9389 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9391 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9395 // VIK_UNITS_DISTANCE_KILOMETRES
9396 if (distance >= 1000 && distance < 100000) {
9397 g_sprintf(str, "%3.2f km", distance/1000.0);
9398 } else if (distance < 1000) {
9399 g_sprintf(str, "%d m", (int)distance);
9401 g_sprintf(str, "%d km", (int)distance/1000);
9405 return g_strdup (str);
9409 * Actually set the message in statusbar
9411 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9413 // Only show elevation data when track has some elevation properties
9414 gchar str_gain_loss[64];
9415 str_gain_loss[0] = '\0';
9416 gchar str_last_step[64];
9417 str_last_step[0] = '\0';
9418 gchar *str_total = distance_string (distance);
9420 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9421 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9422 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9424 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9427 if ( last_step > 0 ) {
9428 gchar *tmp = distance_string (last_step);
9429 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9433 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9435 // Write with full gain/loss information
9436 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9437 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9439 g_free ( str_total );
9443 * Figure out what information should be set in the statusbar and then write it
9445 static void update_statusbar ( VikTrwLayer *vtl )
9447 // Get elevation data
9448 gdouble elev_gain, elev_loss;
9449 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9451 /* Find out actual distance of current track */
9452 gdouble distance = vik_track_get_length (vtl->current_track);
9454 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9458 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9460 /* if we haven't sync'ed yet, we don't have time to do more. */
9461 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9462 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9464 static GdkPixmap *pixmap = NULL;
9466 // Need to check in case window has been resized
9467 w1 = vik_viewport_get_width(vvp);
9468 h1 = vik_viewport_get_height(vvp);
9470 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9472 gdk_drawable_get_size (pixmap, &w2, &h2);
9473 if (w1 != w2 || h1 != h2) {
9474 g_object_unref ( G_OBJECT ( pixmap ) );
9475 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9478 // Reset to background
9479 gdk_draw_drawable (pixmap,
9480 vtl->current_track_newpoint_gc,
9481 vik_viewport_get_pixmap(vvp),
9482 0, 0, 0, 0, -1, -1);
9484 draw_sync_t *passalong;
9487 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9489 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9490 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9491 // thus when we come to reset to the background it would include what we have already drawn!!
9492 gdk_draw_line ( pixmap,
9493 vtl->current_track_newpoint_gc,
9494 x1, y1, event->x, event->y );
9495 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9497 /* Find out actual distance of current track */
9498 gdouble distance = vik_track_get_length (vtl->current_track);
9500 // Now add distance to where the pointer is //
9503 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9504 vik_coord_to_latlon ( &coord, &ll );
9505 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9506 distance = distance + last_step;
9508 // Get elevation data
9509 gdouble elev_gain, elev_loss;
9510 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9512 // Adjust elevation data (if available) for the current pointer position
9514 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9515 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9516 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9517 // Adjust elevation of last track point
9518 if ( elev_new > last_tpt->altitude )
9520 elev_gain += elev_new - last_tpt->altitude;
9523 elev_loss += last_tpt->altitude - elev_new;
9528 // Display of the distance 'tooltip' during track creation is controlled by a preference
9530 if ( a_vik_get_create_track_tooltip() ) {
9532 gchar *str = distance_string (distance);
9534 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9535 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9536 pango_layout_set_text (pl, str, -1);
9538 pango_layout_get_pixel_size ( pl, &wd, &hd );
9541 // offset from cursor a bit depending on font size
9545 // Create a background block to make the text easier to read over the background map
9546 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9547 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9548 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9550 g_object_unref ( G_OBJECT ( pl ) );
9551 g_object_unref ( G_OBJECT ( background_block_gc ) );
9555 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9556 passalong->vtl = vtl;
9557 passalong->pixmap = pixmap;
9558 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9559 passalong->gc = vtl->current_track_newpoint_gc;
9563 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9565 // Update statusbar with full gain/loss information
9566 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9568 // draw pixmap when we have time to
9569 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9570 vtl->draw_sync_done = FALSE;
9571 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9573 return VIK_LAYER_TOOL_ACK;
9576 // NB vtl->current_track must be valid
9577 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9580 if ( vtl->current_track->trackpoints ) {
9581 // TODO rework this...
9582 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9583 GList *last = g_list_last(vtl->current_track->trackpoints);
9584 g_free ( last->data );
9585 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9587 vik_track_calculate_bounds ( vtl->current_track );
9591 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9593 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9594 vtl->current_track = NULL;
9595 vik_layer_emit_update ( VIK_LAYER(vtl) );
9597 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9598 undo_trackpoint_add ( vtl );
9599 update_statusbar ( vtl );
9600 vik_layer_emit_update ( VIK_LAYER(vtl) );
9607 * Common function to handle trackpoint button requests on either a route or a track
9608 * . enables adding a point via normal click
9609 * . enables removal of last point via right click
9610 * . finishing of the track or route via double clicking
9612 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9616 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9619 if ( event->button == 2 ) {
9620 // As the display is panning, the new track pixmap is now invalid so don't draw it
9621 // otherwise this drawing done results in flickering back to an old image
9622 vtl->draw_sync_do = FALSE;
9626 if ( event->button == 3 )
9628 if ( !vtl->current_track )
9630 undo_trackpoint_add ( vtl );
9631 update_statusbar ( vtl );
9632 vik_layer_emit_update ( VIK_LAYER(vtl) );
9636 if ( event->type == GDK_2BUTTON_PRESS )
9638 /* subtract last (duplicate from double click) tp then end */
9639 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9641 /* undo last, then end */
9642 undo_trackpoint_add ( vtl );
9643 vtl->current_track = NULL;
9645 vik_layer_emit_update ( VIK_LAYER(vtl) );
9649 tp = vik_trackpoint_new();
9650 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9652 /* snap to other TP */
9653 if ( event->state & GDK_CONTROL_MASK )
9655 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9657 tp->coord = other_tp->coord;
9660 tp->newsegment = FALSE;
9661 tp->has_timestamp = FALSE;
9664 if ( vtl->current_track ) {
9665 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9666 /* Auto attempt to get elevation from DEM data (if it's available) */
9667 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9670 vtl->ct_x1 = vtl->ct_x2;
9671 vtl->ct_y1 = vtl->ct_y2;
9672 vtl->ct_x2 = event->x;
9673 vtl->ct_y2 = event->y;
9675 vik_layer_emit_update ( VIK_LAYER(vtl) );
9679 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9681 // if we were running the route finder, cancel it
9682 vtl->route_finder_started = FALSE;
9684 // ----------------------------------------------------- if current is a route - switch to new track
9685 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9687 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9688 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9690 new_track_create_common ( vtl, name );
9696 return tool_new_track_or_route_click ( vtl, event, vvp );
9699 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9701 if ( event->button == 2 ) {
9702 // Pan moving ended - enable potential point drawing again
9703 vtl->draw_sync_do = TRUE;
9704 vtl->draw_sync_done = TRUE;
9708 /*** New route ****/
9710 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9715 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9717 // if we were running the route finder, cancel it
9718 vtl->route_finder_started = FALSE;
9720 // -------------------------- if current is a track - switch to new route,
9721 if ( event->button == 1 && ( ! vtl->current_track ||
9722 (vtl->current_track && !vtl->current_track->is_route ) ) )
9724 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9725 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9726 new_route_create_common ( vtl, name );
9732 return tool_new_track_or_route_click ( vtl, event, vvp );
9735 /*** New waypoint ****/
9737 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9742 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9745 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9747 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9748 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9749 trw_layer_calculate_bounds_waypoints ( vtl );
9750 vik_layer_emit_update ( VIK_LAYER(vtl) );
9756 /*** Edit trackpoint ****/
9758 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9760 tool_ed_t *t = g_new(tool_ed_t, 1);
9766 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9771 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9773 tool_ed_t *t = data;
9774 VikViewport *vvp = t->vvp;
9775 TPSearchParams params;
9776 /* OUTDATED DOCUMENTATION:
9777 find 5 pixel range on each side. then put these UTM, and a pointer
9778 to the winning track name (and maybe the winning track itself), and a
9779 pointer to the winning trackpoint, inside an array or struct. pass
9780 this along, do a foreach on the tracks which will do a foreach on the
9783 params.x = event->x;
9784 params.y = event->y;
9785 params.closest_track_id = NULL;
9786 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9787 params.closest_tp = NULL;
9788 params.closest_tpl = NULL;
9789 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9791 if ( event->button != 1 )
9794 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9797 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9800 if ( vtl->current_tpl )
9802 /* 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.) */
9803 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9804 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9809 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9811 if ( current_tr->visible &&
9812 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9813 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9814 marker_begin_move ( t, event->x, event->y );
9820 if ( vtl->tracks_visible )
9821 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9823 if ( params.closest_tp )
9825 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9826 vtl->current_tpl = params.closest_tpl;
9827 vtl->current_tp_id = params.closest_track_id;
9828 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9829 trw_layer_tpwin_init ( vtl );
9830 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9831 vik_layer_emit_update ( VIK_LAYER(vtl) );
9835 if ( vtl->routes_visible )
9836 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9838 if ( params.closest_tp )
9840 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9841 vtl->current_tpl = params.closest_tpl;
9842 vtl->current_tp_id = params.closest_track_id;
9843 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9844 trw_layer_tpwin_init ( vtl );
9845 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9846 vik_layer_emit_update ( VIK_LAYER(vtl) );
9850 /* these aren't the droids you're looking for */
9854 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9856 tool_ed_t *t = data;
9857 VikViewport *vvp = t->vvp;
9859 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9865 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9868 if ( event->state & GDK_CONTROL_MASK )
9870 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9871 if ( tp && tp != vtl->current_tpl->data )
9872 new_coord = tp->coord;
9874 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9877 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9878 marker_moveto ( t, x, y );
9886 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9888 tool_ed_t *t = data;
9889 VikViewport *vvp = t->vvp;
9891 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9893 if ( event->button != 1)
9898 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9901 if ( event->state & GDK_CONTROL_MASK )
9903 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9904 if ( tp && tp != vtl->current_tpl->data )
9905 new_coord = tp->coord;
9908 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9909 if ( vtl->current_tp_track )
9910 vik_track_calculate_bounds ( vtl->current_tp_track );
9912 marker_end_move ( t );
9914 /* diff dist is diff from orig */
9916 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9918 vik_layer_emit_update ( VIK_LAYER(vtl) );
9925 /*** Extended Route Finder ***/
9927 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9932 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9935 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9938 vik_layer_emit_update ( VIK_LAYER(vtl) );
9940 /* remove last ' to:...' */
9941 if ( vtl->current_track->comment ) {
9942 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9943 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9944 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9945 last_to - vtl->current_track->comment - 1);
9946 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9953 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9956 if ( !vtl ) return FALSE;
9957 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9958 if ( event->button == 3 && vtl->current_track ) {
9959 tool_extended_route_finder_undo ( vtl );
9961 else if ( event->button == 2 ) {
9962 vtl->draw_sync_do = FALSE;
9965 // if we started the track but via undo deleted all the track points, begin again
9966 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
9967 return tool_new_track_or_route_click ( vtl, event, vvp );
9969 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
9970 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
9971 struct LatLon start, end;
9973 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
9974 vik_coord_to_latlon ( &(tp_start->coord), &start );
9975 vik_coord_to_latlon ( &(tmp), &end );
9977 vtl->route_finder_started = TRUE;
9978 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9980 // update UI to let user know what's going on
9981 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9982 VikRoutingEngine *engine = vik_routing_default_engine ( );
9984 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
9987 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
9988 vik_routing_engine_get_label ( engine ),
9989 start.lat, start.lon, end.lat, end.lon );
9990 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
9992 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
9995 /* Give GTK a change to display the new status bar before querying the web */
9996 while ( gtk_events_pending ( ) )
9997 gtk_main_iteration ( );
9999 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10001 /* Update UI to say we're done */
10002 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10003 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10004 vik_routing_engine_get_label ( engine ),
10005 start.lat, start.lon, end.lat, end.lon )
10006 : g_strdup_printf ( _("Error getting route from %s."),
10007 vik_routing_engine_get_label ( engine ) );
10008 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10011 vik_layer_emit_update ( VIK_LAYER(vtl) );
10013 vtl->current_track = NULL;
10015 // create a new route where we will add the planned route to
10016 gboolean ret = tool_new_route_click( vtl, event, vvp );
10018 vtl->route_finder_started = TRUE;
10025 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10027 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10028 vtl->route_finder_started = FALSE;
10029 vtl->current_track = NULL;
10030 vik_layer_emit_update ( VIK_LAYER(vtl) );
10032 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10033 tool_extended_route_finder_undo ( vtl );
10040 /*** Show picture ****/
10042 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10047 /* Params are: vvp, event, last match found or NULL */
10048 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10050 if ( wp->image && wp->visible )
10052 gint x, y, slackx, slacky;
10053 GdkEventButton *event = (GdkEventButton *) params[1];
10055 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10056 slackx = wp->image_width / 2;
10057 slacky = wp->image_height / 2;
10058 if ( x <= event->x + slackx && x >= event->x - slackx
10059 && y <= event->y + slacky && y >= event->y - slacky )
10061 params[2] = wp->image; /* we've found a match. however continue searching
10062 * since we want to find the last match -- that
10063 * is, the match that was drawn last. */
10068 static void trw_layer_show_picture ( menu_array_sublayer values )
10070 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10072 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10073 #else /* WINDOWS */
10074 GError *err = NULL;
10075 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10076 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10077 g_free ( quoted_file );
10078 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10080 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() );
10081 g_error_free ( err );
10084 #endif /* WINDOWS */
10087 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10089 gpointer params[3] = { vvp, event, NULL };
10090 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10092 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10095 static menu_array_sublayer values;
10096 values[MA_VTL] = vtl;
10097 values[MA_MISC] = params[2];
10098 trw_layer_show_picture ( values );
10099 return TRUE; /* found a match */
10102 return FALSE; /* go through other layers, searching for a match */
10105 /***************************************************************************
10107 ***************************************************************************/
10110 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10112 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10113 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10116 /* Structure for thumbnail creating data used in the background thread */
10118 VikTrwLayer *vtl; // Layer needed for redrawing
10119 GSList *pics; // Image list
10120 } thumbnail_create_thread_data;
10122 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10124 guint total = g_slist_length(tctd->pics), done = 0;
10125 while ( tctd->pics )
10127 a_thumbnails_create ( (gchar *) tctd->pics->data );
10128 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10130 return -1; /* Abort thread */
10132 tctd->pics = tctd->pics->next;
10135 // Redraw to show the thumbnails as they are now created
10136 if ( IS_VIK_LAYER(tctd->vtl) )
10137 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10142 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10144 while ( tctd->pics )
10146 g_free ( tctd->pics->data );
10147 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10152 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10154 if ( ! vtl->has_verified_thumbnails )
10156 GSList *pics = NULL;
10157 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10160 gint len = g_slist_length ( pics );
10161 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10162 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10165 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10167 (vik_thr_func) create_thumbnails_thread,
10169 (vik_thr_free_func) thumbnail_create_thread_free,
10177 static const gchar* my_track_colors ( gint ii )
10179 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10191 // Fast and reliable way of returning a colour
10192 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10195 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10197 GHashTableIter iter;
10198 gpointer key, value;
10202 g_hash_table_iter_init ( &iter, vtl->tracks );
10204 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10206 // Tracks get a random spread of colours if not already assigned
10207 if ( ! VIK_TRACK(value)->has_color ) {
10208 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10209 VIK_TRACK(value)->color = vtl->track_color;
10211 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10213 VIK_TRACK(value)->has_color = TRUE;
10216 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10219 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10225 g_hash_table_iter_init ( &iter, vtl->routes );
10227 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10229 // Routes get an intermix of reds
10230 if ( ! VIK_TRACK(value)->has_color ) {
10232 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10234 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10235 VIK_TRACK(value)->has_color = TRUE;
10238 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10245 * (Re)Calculate the bounds of the waypoints in this layer,
10246 * This should be called whenever waypoints are changed
10248 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10250 struct LatLon topleft = { 0.0, 0.0 };
10251 struct LatLon bottomright = { 0.0, 0.0 };
10254 GHashTableIter iter;
10255 gpointer key, value;
10257 g_hash_table_iter_init ( &iter, vtl->waypoints );
10259 // Set bounds to first point
10260 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10261 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10262 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10265 // Ensure there is another point...
10266 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10268 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10270 // See if this point increases the bounds.
10271 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10273 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10274 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10275 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10276 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10280 vtl->waypoints_bbox.north = topleft.lat;
10281 vtl->waypoints_bbox.east = bottomright.lon;
10282 vtl->waypoints_bbox.south = bottomright.lat;
10283 vtl->waypoints_bbox.west = topleft.lon;
10286 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10288 vik_track_calculate_bounds ( trk );
10291 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10293 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10294 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10297 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10299 if ( ! VIK_LAYER(vtl)->vt )
10302 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10303 if ( g_hash_table_size (vtl->tracks) > 1 )
10304 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10306 if ( g_hash_table_size (vtl->routes) > 1 )
10307 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10309 if ( g_hash_table_size (vtl->waypoints) > 1 )
10310 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10313 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10315 if ( VIK_LAYER(vtl)->realized )
10316 trw_layer_verify_thumbnails ( vtl, vvp );
10317 trw_layer_track_alloc_colors ( vtl );
10319 trw_layer_calculate_bounds_waypoints ( vtl );
10320 trw_layer_calculate_bounds_tracks ( vtl );
10322 // Apply treeview sort after loading all the tracks for this layer
10323 // (rather than sorted insert on each individual track additional)
10324 // and after subsequent changes to the properties as the specified order may have changed.
10325 // since the sorting of a treeview section is now very quick
10326 // NB sorting is also performed after every name change as well to maintain the list order
10327 trw_layer_sort_all ( vtl );
10329 // Setting metadata time if not otherwise set
10330 if ( vtl->metadata ) {
10332 gboolean need_to_set_time = TRUE;
10333 if ( vtl->metadata->timestamp ) {
10334 need_to_set_time = FALSE;
10335 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10336 need_to_set_time = TRUE;
10339 if ( need_to_set_time ) {
10340 // Could rewrite this as a general get first time of a TRW Layer function
10341 GTimeVal timestamp;
10342 timestamp.tv_usec = 0;
10343 gboolean has_timestamp = FALSE;
10346 gl = g_hash_table_get_values ( vtl->tracks );
10347 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10348 gl = g_list_first ( gl );
10350 // Check times of tracks
10352 // Only need to check the first track as they have been sorted by time
10353 VikTrack *trk = (VikTrack*)gl->data;
10354 // Assume trackpoints already sorted by time
10355 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10356 if ( tpt && tpt->has_timestamp ) {
10357 timestamp.tv_sec = tpt->timestamp;
10358 has_timestamp = TRUE;
10360 g_list_free ( gl );
10363 if ( !has_timestamp ) {
10364 // 'Last' resort - current time
10365 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10366 g_get_current_time ( ×tamp );
10368 // Check times of waypoints
10369 gl = g_hash_table_get_values ( vtl->waypoints );
10371 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10372 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10373 if ( wpt->has_timestamp ) {
10374 if ( timestamp.tv_sec > wpt->timestamp ) {
10375 timestamp.tv_sec = wpt->timestamp;
10376 has_timestamp = TRUE;
10380 g_list_free ( gl );
10383 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10388 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10390 return vtl->coord_mode;
10394 * Uniquify the whole layer
10395 * Also requires the layers panel as the names shown there need updating too
10396 * Returns whether the operation was successful or not
10398 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10400 if ( vtl && vlp ) {
10401 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10402 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10403 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10409 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10411 vik_coord_convert ( &(wp->coord), *dest_mode );
10414 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10416 vik_track_convert ( tr, *dest_mode );
10419 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10421 if ( vtl->coord_mode != dest_mode )
10423 vtl->coord_mode = dest_mode;
10424 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10425 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10426 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10430 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10432 vtl->menu_selection = selection;
10435 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10437 return (vtl->menu_selection);
10440 /* ----------- Downloading maps along tracks --------------- */
10442 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10444 /* TODO: calculating based on current size of viewport */
10445 const gdouble w_at_zoom_0_125 = 0.0013;
10446 const gdouble h_at_zoom_0_125 = 0.0011;
10447 gdouble zoom_factor = zoom_level/0.125;
10449 wh->lat = h_at_zoom_0_125 * zoom_factor;
10450 wh->lon = w_at_zoom_0_125 * zoom_factor;
10452 return 0; /* all OK */
10455 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10457 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10458 (dist->lat >= ABS(to->north_south - from->north_south)))
10461 VikCoord *coord = g_malloc(sizeof(VikCoord));
10462 coord->mode = VIK_COORD_LATLON;
10464 if (ABS(gradient) < 1) {
10465 if (from->east_west > to->east_west)
10466 coord->east_west = from->east_west - dist->lon;
10468 coord->east_west = from->east_west + dist->lon;
10469 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10471 if (from->north_south > to->north_south)
10472 coord->north_south = from->north_south - dist->lat;
10474 coord->north_south = from->north_south + dist->lat;
10475 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10481 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10483 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10484 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10486 VikCoord *next = from;
10488 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10490 list = g_list_prepend(list, next);
10496 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10498 typedef struct _Rect {
10503 #define GLRECT(iter) ((Rect *)((iter)->data))
10506 GList *rects_to_download = NULL;
10509 if (get_download_area_width(vvp, zoom_level, &wh))
10512 GList *iter = tr->trackpoints;
10516 gboolean new_map = TRUE;
10517 VikCoord *cur_coord, tl, br;
10520 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10522 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10523 rect = g_malloc(sizeof(Rect));
10526 rect->center = *cur_coord;
10527 rects_to_download = g_list_prepend(rects_to_download, rect);
10532 gboolean found = FALSE;
10533 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10534 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10545 GList *fillins = NULL;
10546 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10547 /* seems that ATM the function get_next_coord works only for LATLON */
10548 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10549 /* fill-ins for far apart points */
10550 GList *cur_rect, *next_rect;
10551 for (cur_rect = rects_to_download;
10552 (next_rect = cur_rect->next) != NULL;
10553 cur_rect = cur_rect->next) {
10554 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10555 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10556 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10560 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10563 GList *fiter = fillins;
10565 cur_coord = (VikCoord *)(fiter->data);
10566 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10567 rect = g_malloc(sizeof(Rect));
10570 rect->center = *cur_coord;
10571 rects_to_download = g_list_prepend(rects_to_download, rect);
10572 fiter = fiter->next;
10576 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10577 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10581 for (iter = fillins; iter; iter = iter->next)
10582 g_free(iter->data);
10583 g_list_free(fillins);
10585 if (rects_to_download) {
10586 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10587 g_free(rect_iter->data);
10588 g_list_free(rects_to_download);
10592 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10596 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10597 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10598 gint selected_zoom, default_zoom;
10600 VikTrwLayer *vtl = values[MA_VTL];
10601 VikLayersPanel *vlp = values[MA_VLP];
10603 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10604 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10606 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10610 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10612 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10613 int num_maps = g_list_length(vmls);
10616 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10620 // Convert from list of vmls to list of names. Allowing the user to select one of them
10621 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10622 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10624 gchar **np = map_names;
10625 VikMapsLayer **lp = map_layers;
10627 for (i = 0; i < num_maps; i++) {
10628 vml = (VikMapsLayer *)(vmls->data);
10630 *np++ = vik_maps_layer_get_map_label(vml);
10633 // Mark end of the array lists
10637 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10638 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10639 if (cur_zoom == zoom_vals[default_zoom])
10642 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10644 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10647 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10650 for (i = 0; i < num_maps; i++)
10651 g_free(map_names[i]);
10653 g_free(map_layers);
10659 /**** lowest waypoint number calculation ***/
10660 static gint highest_wp_number_name_to_number(const gchar *name) {
10661 if ( strlen(name) == 3 ) {
10662 int n = atoi(name);
10663 if ( n < 100 && name[0] != '0' )
10665 if ( n < 10 && name[0] != '0' )
10673 static void highest_wp_number_reset(VikTrwLayer *vtl)
10675 vtl->highest_wp_number = -1;
10678 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10680 /* if is bigger that top, add it */
10681 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10682 if ( new_wp_num > vtl->highest_wp_number )
10683 vtl->highest_wp_number = new_wp_num;
10686 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10688 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10689 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10690 if ( vtl->highest_wp_number == old_wp_num ) {
10692 vtl->highest_wp_number--;
10694 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10695 /* search down until we find something that *does* exist */
10697 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10698 vtl->highest_wp_number--;
10699 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10704 /* get lowest unused number */
10705 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10708 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10710 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10711 return g_strdup(buf);
10715 * trw_layer_create_track_list_both:
10717 * Create the latest list of tracks and routes
10719 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10721 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10722 GList *tracks = NULL;
10723 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10724 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10726 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10729 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10731 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10733 gchar *title = NULL;
10734 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10735 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10737 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10739 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10743 static void trw_layer_track_list_dialog ( menu_array_layer values )
10745 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10747 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10748 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10752 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10754 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10756 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10757 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );