2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
34 #include "vikmapslayer.h"
35 #include "vikgpslayer.h"
36 #include "viktrwlayer_export.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_propwin.h"
39 #include "viktrwlayer_analysis.h"
40 #include "viktrwlayer_tracklist.h"
41 #include "viktrwlayer_waypointlist.h"
42 #ifdef VIK_CONFIG_GEOTAG
43 #include "viktrwlayer_geotag.h"
44 #include "geotag_exif.h"
46 #include "garminsymbols.h"
47 #include "thumbnails.h"
48 #include "background.h"
53 #include "geonamessearch.h"
54 #ifdef VIK_CONFIG_OPENSTREETMAP
55 #include "osm-traces.h"
58 #include "datasources.h"
59 #include "datasource_gps.h"
60 #include "vikexttool_datasources.h"
64 #include "vikrouting.h"
66 #include "icons/icons.h"
80 #include <gdk/gdkkeysyms.h>
82 #include <glib/gstdio.h>
83 #include <glib/gi18n.h>
85 #define VIK_TRW_LAYER_TRACK_GC 6
86 #define VIK_TRW_LAYER_TRACK_GCS 10
87 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
88 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
89 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
90 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
91 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
92 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
94 #define DRAWMODE_BY_TRACK 0
95 #define DRAWMODE_BY_SPEED 1
96 #define DRAWMODE_ALL_SAME_COLOR 2
97 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
98 // as we are (re)calculating the colour for every point
103 /* this is how it knows when you click if you are clicking close to a trackpoint. */
104 #define TRACKPOINT_SIZE_APPROX 5
105 #define WAYPOINT_SIZE_APPROX 5
107 #define MIN_STOP_LENGTH 15
108 #define MAX_STOP_LENGTH 86400
109 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
110 /* this is multiplied by user-inputted value from 1-100. */
112 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
114 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
116 FS_XX_SMALL = 0, // 'xx-small'
119 FS_MEDIUM, // DEFAULT
126 struct _VikTrwLayer {
129 GHashTable *tracks_iters;
131 GHashTable *routes_iters;
132 GHashTable *waypoints_iters;
133 GHashTable *waypoints;
134 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
135 gboolean tracks_visible, routes_visible, waypoints_visible;
136 LatLonBBox waypoints_bbox;
138 gboolean track_draw_labels;
141 guint8 drawpoints_size;
142 guint8 drawelevation;
143 guint8 elevation_factor;
147 guint8 drawdirections;
148 guint8 drawdirections_size;
149 guint8 line_thickness;
150 guint8 bg_line_thickness;
151 vik_layer_sort_order_t track_sort_order;
154 VikTRWMetadata *metadata;
156 PangoLayout *tracklabellayout;
157 font_size_t track_font_size;
158 gchar *track_fsize_str;
162 gboolean wp_draw_symbols;
163 font_size_t wp_font_size;
165 vik_layer_sort_order_t wp_sort_order;
167 gdouble track_draw_speed_factor;
169 GdkGC *track_1color_gc;
170 GdkColor track_color;
171 GdkGC *current_track_gc;
172 // Separate GC for a track's potential new point as drawn via separate method
173 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
174 GdkGC *current_track_newpoint_gc;
175 GdkGC *track_bg_gc; GdkColor track_bg_color;
176 GdkGC *waypoint_gc; GdkColor waypoint_color;
177 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
178 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
180 GdkFont *waypoint_font;
181 VikTrack *current_track; // ATM shared between new tracks and new routes
182 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
183 gboolean draw_sync_done;
184 gboolean draw_sync_do;
186 VikCoordMode coord_mode;
188 /* wp editing tool */
189 VikWaypoint *current_wp;
190 gpointer current_wp_id;
192 gboolean waypoint_rightclick;
194 /* track editing tool */
196 VikTrack *current_tp_track;
197 gpointer current_tp_id;
198 VikTrwLayerTpwin *tpwin;
200 /* track editing tool -- more specifically, moving tps */
203 /* route finder tool */
204 gboolean route_finder_started;
205 VikCoord route_finder_coord;
206 gboolean route_finder_check_added_track;
207 VikTrack *route_finder_added_track;
208 VikTrack *route_finder_current_track;
209 gboolean route_finder_append;
216 guint16 image_cache_size;
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
221 gboolean has_verified_thumbnails;
223 GtkMenu *wp_right_click_menu;
224 GtkMenu *track_right_click_menu;
227 VikStdLayerMenuItem menu_selection;
229 gint highest_wp_number;
232 GtkWidget *tracks_analysis_dialog;
235 /* A caached waypoint image. */
238 gchar *image; /* filename */
241 struct DrawingParams {
246 guint16 width, height;
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
249 const VikCoord *center;
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
255 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
260 MA_SUBTYPE, // OR END for Layer only
269 typedef gpointer menu_array_layer[2];
270 typedef gpointer menu_array_sublayer[MA_LAST];
272 static void trw_layer_delete_item ( menu_array_sublayer values );
273 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
274 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
276 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
277 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
279 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
280 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
282 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
283 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
285 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
286 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
287 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
290 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_center ( menu_array_sublayer values );
292 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
293 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
294 static void trw_layer_merge_with_other ( menu_array_sublayer values );
295 static void trw_layer_append_track ( menu_array_sublayer values );
296 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
297 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
298 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
299 static void trw_layer_split_segments ( menu_array_sublayer values );
300 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
301 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
303 static void trw_layer_reverse ( menu_array_sublayer values );
304 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
305 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
306 static void trw_layer_show_picture ( menu_array_sublayer values );
307 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
309 static void trw_layer_centerize ( menu_array_layer values );
310 static void trw_layer_auto_view ( menu_array_layer values );
311 static void trw_layer_goto_wp ( menu_array_layer values );
312 static void trw_layer_new_wp ( menu_array_layer values );
313 static void trw_layer_new_track ( menu_array_layer values );
314 static void trw_layer_new_route ( menu_array_layer values );
315 static void trw_layer_finish_track ( menu_array_layer values );
316 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
317 static void trw_layer_auto_tracks_view ( menu_array_layer values );
318 static void trw_layer_delete_all_tracks ( menu_array_layer values );
319 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
320 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
321 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
322 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
324 #ifdef VIK_CONFIG_GEOTAG
325 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
326 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
327 static void trw_layer_geotagging_track ( menu_array_sublayer values );
328 static void trw_layer_geotagging ( menu_array_layer values );
330 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
331 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
332 static void trw_layer_acquire_url_cb ( menu_array_layer values );
333 #ifdef VIK_CONFIG_OPENSTREETMAP
334 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
335 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
337 #ifdef VIK_CONFIG_GEOCACHES
338 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
340 #ifdef VIK_CONFIG_GEOTAG
341 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
343 static void trw_layer_acquire_file_cb ( menu_array_layer values );
344 static void trw_layer_gps_upload ( menu_array_layer values );
346 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
347 static void trw_layer_track_list_dialog ( menu_array_layer values );
348 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
350 // Specific route versions:
351 // Most track handling functions can handle operating on the route list
352 // However these ones are easier in separate functions
353 static void trw_layer_auto_routes_view ( menu_array_layer values );
354 static void trw_layer_delete_all_routes ( menu_array_layer values );
355 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
358 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
359 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
360 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
361 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
363 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
364 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
365 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
367 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
372 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
379 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
389 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
391 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
396 static void cached_pixbuf_free ( CachedPixbuf *cp );
397 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
399 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
400 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
402 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
403 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
405 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
406 static void highest_wp_number_reset(VikTrwLayer *vtl);
407 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
408 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
410 // Note for the following tool GtkRadioActionEntry texts:
411 // the very first text value is an internal name not displayed anywhere
412 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
413 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
414 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
415 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
416 static VikToolInterface trw_layer_tools[] = {
417 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
418 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
419 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
421 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
423 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
424 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
425 (VikToolMouseFunc) tool_new_track_click,
426 (VikToolMouseMoveFunc) tool_new_track_move,
427 (VikToolMouseFunc) tool_new_track_release,
428 (VikToolKeyFunc) tool_new_track_key_press,
429 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
430 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
432 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
433 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
434 (VikToolMouseFunc) tool_new_route_click,
435 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
436 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
437 (VikToolKeyFunc) tool_new_track_key_press, // -/#
438 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
439 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
441 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
442 (VikToolConstructorFunc) tool_edit_waypoint_create,
443 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
445 (VikToolMouseFunc) tool_edit_waypoint_click,
446 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
447 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
449 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
451 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
452 (VikToolConstructorFunc) tool_edit_trackpoint_create,
453 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
455 (VikToolMouseFunc) tool_edit_trackpoint_click,
456 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
457 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
459 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
461 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
462 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
463 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
465 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
467 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
468 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
469 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
471 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
475 TOOL_CREATE_WAYPOINT=0,
479 TOOL_EDIT_TRACKPOINT,
485 /****** PARAMETERS ******/
487 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
488 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
490 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
491 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
493 #define MIN_POINT_SIZE 2
494 #define MAX_POINT_SIZE 10
496 #define MIN_ARROW_SIZE 3
497 #define MAX_ARROW_SIZE 20
499 static VikLayerParamScale params_scales[] = {
500 /* min max step digits */
501 { 1, 10, 1, 0 }, /* line_thickness */
502 { 0, 100, 1, 0 }, /* track draw speed factor */
503 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
504 /* 5 * step == how much to turn */
505 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
506 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
507 { 5, 500, 5, 0 }, // 5: image cache_size - " "
508 { 0, 8, 1, 0 }, // 6: Background line thickness
509 { 1, 64, 1, 0 }, /* wpsize */
510 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
511 { 1, 100, 1, 0 }, // 9: elevation factor
512 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
513 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
516 static gchar* params_font_sizes[] = {
517 N_("Extra Extra Small"),
523 N_("Extra Extra Large"),
526 // Needs to align with vik_layer_sort_order_t
527 static gchar* params_sort_order[] = {
529 N_("Name Ascending"),
530 N_("Name Descending"),
534 static VikLayerParamData black_color_default ( void ) {
535 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
537 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
538 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
539 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
540 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
541 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
542 static VikLayerParamData trackbgcolor_default ( void ) {
543 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
545 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
546 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
547 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
549 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
550 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
551 static VikLayerParamData wptextcolor_default ( void ) {
552 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
554 static VikLayerParamData wpbgcolor_default ( void ) {
555 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
557 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
558 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
560 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
561 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
562 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
564 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
566 static VikLayerParamData string_default ( void )
568 VikLayerParamData data;
573 VikLayerParam trw_layer_params[] = {
574 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
575 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
576 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
578 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
579 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
580 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
581 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
582 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
583 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
584 { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
585 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
586 { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
587 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
588 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
589 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
590 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
591 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
592 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
593 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
594 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
596 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
597 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
598 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
599 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
600 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
602 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
603 { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
604 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
605 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
606 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
607 { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
608 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
609 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
610 { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
611 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
613 { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
614 { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[3], NULL, NULL, image_size_default, NULL, NULL },
615 { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
616 { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
618 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
619 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
620 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
621 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
626 // Sublayer visibilities
674 *** 1) Add to trw_layer_params and enumeration
675 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
678 /****** END PARAMETERS ******/
680 /* Layer Interface function definitions */
681 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
682 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
683 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
684 static void trw_layer_free ( VikTrwLayer *trwlayer );
685 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
686 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
687 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
688 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
689 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
690 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
691 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
692 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
693 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
694 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
695 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
696 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
697 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
698 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
699 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
700 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
701 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
702 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
703 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
704 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
705 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
706 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
707 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
708 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
709 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
710 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
711 /* End Layer Interface function definitions */
713 VikLayerInterface vik_trw_layer_interface = {
720 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
724 params_groups, /* params_groups */
725 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
729 (VikLayerFuncCreate) trw_layer_create,
730 (VikLayerFuncRealize) trw_layer_realize,
731 (VikLayerFuncPostRead) trw_layer_post_read,
732 (VikLayerFuncFree) trw_layer_free,
734 (VikLayerFuncProperties) NULL,
735 (VikLayerFuncDraw) trw_layer_draw,
736 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
738 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
739 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
741 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
742 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
744 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
745 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
746 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
747 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
748 (VikLayerFuncLayerSelected) trw_layer_selected,
750 (VikLayerFuncMarshall) trw_layer_marshall,
751 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
753 (VikLayerFuncSetParam) trw_layer_set_param,
754 (VikLayerFuncGetParam) trw_layer_get_param,
755 (VikLayerFuncChangeParam) trw_layer_change_param,
757 (VikLayerFuncReadFileData) a_gpspoint_read_file,
758 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
760 (VikLayerFuncDeleteItem) trw_layer_del_item,
761 (VikLayerFuncCutItem) trw_layer_cut_item,
762 (VikLayerFuncCopyItem) trw_layer_copy_item,
763 (VikLayerFuncPasteItem) trw_layer_paste_item,
764 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
766 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
768 (VikLayerFuncSelectClick) trw_layer_select_click,
769 (VikLayerFuncSelectMove) trw_layer_select_move,
770 (VikLayerFuncSelectRelease) trw_layer_select_release,
771 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
774 GType vik_trw_layer_get_type ()
776 static GType vtl_type = 0;
780 static const GTypeInfo vtl_info =
782 sizeof (VikTrwLayerClass),
783 NULL, /* base_init */
784 NULL, /* base_finalize */
785 NULL, /* class init */
786 NULL, /* class_finalize */
787 NULL, /* class_data */
788 sizeof (VikTrwLayer),
790 NULL /* instance init */
792 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
798 VikTRWMetadata *vik_trw_metadata_new()
800 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
803 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
808 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
810 return vtl->metadata;
813 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
816 vik_trw_metadata_free ( vtl->metadata );
817 vtl->metadata = metadata;
822 const gchar *date_str;
824 const VikWaypoint *wpt;
829 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
833 // Might be an easier way to compare dates rather than converting the strings all the time...
834 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
835 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
837 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
846 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
850 // Might be an easier way to compare dates rather than converting the strings all the time...
851 if ( wpt->has_timestamp ) {
852 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
854 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
864 * Find an item by date
866 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
870 df.date_str = date_str;
875 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
877 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
879 if ( select && df.found ) {
880 if ( do_tracks && df.trk ) {
881 struct LatLon maxmin[2] = { {0,0}, {0,0} };
882 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
883 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
884 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
887 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord) );
888 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
890 vik_layer_emit_update ( VIK_LAYER(vtl) );
895 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
897 static menu_array_sublayer values;
903 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
906 values[MA_VTL] = vtl;
907 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
908 values[MA_SUBLAYER_ID] = sublayer;
909 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
911 trw_layer_delete_item ( values );
914 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
916 static menu_array_sublayer values;
922 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
925 values[MA_VTL] = vtl;
926 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
927 values[MA_SUBLAYER_ID] = sublayer;
928 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
930 trw_layer_copy_item_cb(values);
931 trw_layer_cut_item_cb(values);
934 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
936 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
937 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
938 gpointer * sublayer = values[MA_SUBLAYER_ID];
942 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
946 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
947 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
948 if ( wp && wp->name )
951 name = NULL; // Broken :(
953 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
954 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
955 if ( trk && trk->name )
958 name = NULL; // Broken :(
961 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
962 if ( trk && trk->name )
965 name = NULL; // Broken :(
968 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
969 subtype, len, name, data);
973 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
975 trw_layer_copy_item_cb(values);
976 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
977 trw_layer_delete_item(values);
980 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
982 // Slightly cheating method, routing via the panels capability
983 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
986 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
996 GByteArray *ba = g_byte_array_new ();
998 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
999 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1000 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1001 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1003 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1006 g_byte_array_append ( ba, id, il );
1014 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1021 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1025 w = vik_waypoint_unmarshall ( item, len );
1026 // When copying - we'll create a new name based on the original
1027 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1028 vik_trw_layer_add_waypoint ( vtl, name, w );
1029 waypoint_convert (NULL, w, &vtl->coord_mode);
1032 trw_layer_calculate_bounds_waypoints ( vtl );
1034 // Consider if redraw necessary for the new item
1035 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1036 vik_layer_emit_update ( VIK_LAYER(vtl) );
1039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1043 t = vik_track_unmarshall ( item, len );
1044 // When copying - we'll create a new name based on the original
1045 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1046 vik_trw_layer_add_track ( vtl, name, t );
1047 vik_track_convert (t, vtl->coord_mode);
1050 // Consider if redraw necessary for the new item
1051 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1052 vik_layer_emit_update ( VIK_LAYER(vtl) );
1055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1059 t = vik_track_unmarshall ( item, len );
1060 // When copying - we'll create a new name based on the original
1061 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1062 vik_trw_layer_add_route ( vtl, name, t );
1063 vik_track_convert (t, vtl->coord_mode);
1066 // Consider if redraw necessary for the new item
1067 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1068 vik_layer_emit_update ( VIK_LAYER(vtl) );
1074 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1081 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1085 case PARAM_TV: vtl->tracks_visible = data.b; break;
1086 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1087 case PARAM_RV: vtl->routes_visible = data.b; break;
1088 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1089 case PARAM_TLFONTSIZE:
1090 if ( data.u < FS_NUM_SIZES ) {
1091 vtl->track_font_size = data.u;
1092 g_free ( vtl->track_fsize_str );
1093 switch ( vtl->track_font_size ) {
1094 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1095 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1096 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1097 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1098 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1099 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1100 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1104 case PARAM_DM: vtl->drawmode = data.u; break;
1106 vtl->track_color = data.c;
1107 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1109 case PARAM_DP: vtl->drawpoints = data.b; break;
1111 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1112 vtl->drawpoints_size = data.u;
1114 case PARAM_DE: vtl->drawelevation = data.b; break;
1115 case PARAM_DS: vtl->drawstops = data.b; break;
1116 case PARAM_DL: vtl->drawlines = data.b; break;
1117 case PARAM_DD: vtl->drawdirections = data.b; break;
1119 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1120 vtl->drawdirections_size = data.u;
1122 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1123 vtl->stop_length = data.u;
1125 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1126 vtl->elevation_factor = data.u;
1128 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1130 vtl->line_thickness = data.u;
1131 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1134 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1136 vtl->bg_line_thickness = data.u;
1137 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1141 vtl->track_bg_color = data.c;
1142 if ( vtl->track_bg_gc )
1143 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1145 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1146 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1147 case PARAM_DLA: vtl->drawlabels = data.b; break;
1148 case PARAM_DI: vtl->drawimages = data.b; break;
1149 case PARAM_IS: if ( data.u != vtl->image_size )
1151 vtl->image_size = data.u;
1152 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1153 g_queue_free ( vtl->image_cache );
1154 vtl->image_cache = g_queue_new ();
1157 case PARAM_IA: vtl->image_alpha = data.u; break;
1158 case PARAM_ICS: vtl->image_cache_size = data.u;
1159 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1160 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1163 vtl->waypoint_color = data.c;
1164 if ( vtl->waypoint_gc )
1165 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1168 vtl->waypoint_text_color = data.c;
1169 if ( vtl->waypoint_text_gc )
1170 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1173 vtl->waypoint_bg_color = data.c;
1174 if ( vtl->waypoint_bg_gc )
1175 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1178 vtl->wpbgand = data.b;
1179 if ( vtl->waypoint_bg_gc )
1180 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1182 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1183 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1184 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1185 case PARAM_WPFONTSIZE:
1186 if ( data.u < FS_NUM_SIZES ) {
1187 vtl->wp_font_size = data.u;
1188 g_free ( vtl->wp_fsize_str );
1189 switch ( vtl->wp_font_size ) {
1190 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1191 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1192 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1193 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1194 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1195 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1196 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1200 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1202 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1203 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1204 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1205 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1211 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1213 VikLayerParamData rv;
1216 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1217 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1218 case PARAM_RV: rv.b = vtl->routes_visible; break;
1219 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1220 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1221 case PARAM_DM: rv.u = vtl->drawmode; break;
1222 case PARAM_TC: rv.c = vtl->track_color; break;
1223 case PARAM_DP: rv.b = vtl->drawpoints; break;
1224 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1225 case PARAM_DE: rv.b = vtl->drawelevation; break;
1226 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1227 case PARAM_DS: rv.b = vtl->drawstops; break;
1228 case PARAM_SL: rv.u = vtl->stop_length; break;
1229 case PARAM_DL: rv.b = vtl->drawlines; break;
1230 case PARAM_DD: rv.b = vtl->drawdirections; break;
1231 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1232 case PARAM_LT: rv.u = vtl->line_thickness; break;
1233 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1234 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1235 case PARAM_DI: rv.b = vtl->drawimages; break;
1236 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1237 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1238 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1239 case PARAM_IS: rv.u = vtl->image_size; break;
1240 case PARAM_IA: rv.u = vtl->image_alpha; break;
1241 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1242 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1243 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1244 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1245 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1246 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1247 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1248 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1249 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1250 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1252 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1253 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1254 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1255 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1261 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1263 // This '-3' is to account for the first few parameters not in the properties
1264 const gint OFFSET = -3;
1266 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1267 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1270 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1271 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1272 GtkWidget **ww2 = values[UI_CHG_LABELS];
1273 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1274 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1275 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1276 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1277 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1278 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1279 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1280 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1281 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1282 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1283 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1284 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1287 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1290 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1291 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1292 GtkWidget **ww2 = values[UI_CHG_LABELS];
1293 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1294 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1295 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1296 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1297 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1298 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1299 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1300 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1301 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1302 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1303 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1304 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1305 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1306 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1307 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1308 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1311 // Alter sensitivity of all track colours according to the draw track mode.
1314 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1315 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1316 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1317 GtkWidget **ww2 = values[UI_CHG_LABELS];
1318 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1319 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1320 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1321 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1324 case PARAM_MDTIME: {
1325 // Force metadata->timestamp to be always read-only for now.
1326 GtkWidget **ww = values[UI_CHG_WIDGETS];
1327 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1328 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1330 // NB Since other track settings have been split across tabs,
1331 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1336 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1343 // Use byte arrays to store sublayer data
1344 // much like done elsewhere e.g. vik_layer_marshall_params()
1345 GByteArray *ba = g_byte_array_new ( );
1350 guint object_length;
1353 // the length of the item
1354 // the sublayer type of item
1355 // the the actual item
1356 #define tlm_append(object_pointer, size, type) \
1358 object_length = (size); \
1359 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1360 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1361 g_byte_array_append ( ba, (object_pointer), object_length );
1363 // Layer parameters first
1364 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1365 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1366 g_byte_array_append ( ba, pd, pl );
1369 // Now sublayer data
1370 GHashTableIter iter;
1371 gpointer key, value;
1374 g_hash_table_iter_init ( &iter, vtl->waypoints );
1375 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1376 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1377 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1382 g_hash_table_iter_init ( &iter, vtl->tracks );
1383 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1384 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1385 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1390 g_hash_table_iter_init ( &iter, vtl->routes );
1391 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1392 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1393 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1403 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1405 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1407 gint consumed_length;
1409 // First the overall layer parameters
1410 memcpy(&pl, data, sizeof(pl));
1412 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1415 consumed_length = pl;
1416 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1418 #define tlm_size (*(gint *)data)
1419 // See marshalling above for order of how this is written
1421 data += sizeof_len_and_subtype + tlm_size;
1423 // Now the individual sublayers:
1425 while ( *data && consumed_length < len ) {
1426 // Normally four extra bytes at the end of the datastream
1427 // (since it's a GByteArray and that's where it's length is stored)
1428 // So only attempt read when there's an actual block of sublayer data
1429 if ( consumed_length + tlm_size < len ) {
1431 // Reuse pl to read the subtype from the data stream
1432 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1434 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1435 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1436 gchar *name = g_strdup ( trk->name );
1437 vik_trw_layer_add_track ( vtl, name, trk );
1440 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1441 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1442 gchar *name = g_strdup ( wp->name );
1443 vik_trw_layer_add_waypoint ( vtl, name, wp );
1446 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1447 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1448 gchar *name = g_strdup ( trk->name );
1449 vik_trw_layer_add_route ( vtl, name, trk );
1453 consumed_length += tlm_size + sizeof_len_and_subtype;
1456 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1458 // Not stored anywhere else so need to regenerate
1459 trw_layer_calculate_bounds_waypoints ( vtl );
1464 // Keep interesting hash function at least visible
1466 static guint strcase_hash(gconstpointer v)
1468 // 31 bit hash function
1471 gchar s[128]; // malloc is too slow for reading big files
1474 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1475 p[i] = toupper(t[i]);
1481 for (p += 1; *p != '\0'; p++)
1482 h = (h << 5) - h + *p;
1489 // Stick a 1 at the end of the function name to make it more unique
1490 // thus more easily searchable in a simple text editor
1491 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1493 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1494 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1496 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1497 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1499 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1500 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1501 // and with normal PC processing capabilities - it has negligibile performance impact
1502 // This also minimized the amount of rework - as the management of the hash tables already exists.
1504 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1505 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1506 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1508 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1509 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1510 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1511 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1512 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1513 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1515 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1517 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1519 // Param settings that are not available via the GUI
1520 // Force to on after processing params (which defaults them to off with a zero value)
1521 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1523 rv->metadata = vik_trw_metadata_new ();
1524 rv->draw_sync_done = TRUE;
1525 rv->draw_sync_do = TRUE;
1526 // Everything else is 0, FALSE or NULL
1532 static void trw_layer_free ( VikTrwLayer *trwlayer )
1534 g_hash_table_destroy(trwlayer->waypoints);
1535 g_hash_table_destroy(trwlayer->waypoints_iters);
1536 g_hash_table_destroy(trwlayer->tracks);
1537 g_hash_table_destroy(trwlayer->tracks_iters);
1538 g_hash_table_destroy(trwlayer->routes);
1539 g_hash_table_destroy(trwlayer->routes_iters);
1541 /* ODC: replace with GArray */
1542 trw_layer_free_track_gcs ( trwlayer );
1544 if ( trwlayer->wp_right_click_menu )
1545 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1547 if ( trwlayer->track_right_click_menu )
1548 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1550 if ( trwlayer->tracklabellayout != NULL)
1551 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1553 if ( trwlayer->wplabellayout != NULL)
1554 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1556 if ( trwlayer->waypoint_gc != NULL )
1557 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1559 if ( trwlayer->waypoint_text_gc != NULL )
1560 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1562 if ( trwlayer->waypoint_bg_gc != NULL )
1563 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1565 g_free ( trwlayer->wp_fsize_str );
1566 g_free ( trwlayer->track_fsize_str );
1568 if ( trwlayer->tpwin != NULL )
1569 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1571 if ( trwlayer->tracks_analysis_dialog != NULL )
1572 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1574 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1575 g_queue_free ( trwlayer->image_cache );
1578 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1582 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1583 dp->xmpp = vik_viewport_get_xmpp ( vp );
1584 dp->ympp = vik_viewport_get_ympp ( vp );
1585 dp->width = vik_viewport_get_width ( vp );
1586 dp->height = vik_viewport_get_height ( vp );
1587 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1588 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1590 dp->center = vik_viewport_get_center ( vp );
1591 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1592 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1597 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1598 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1599 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1601 dp->ce1 = dp->center->east_west-w2;
1602 dp->ce2 = dp->center->east_west+w2;
1603 dp->cn1 = dp->center->north_south-h2;
1604 dp->cn2 = dp->center->north_south+h2;
1605 } else if ( dp->lat_lon ) {
1606 VikCoord upperleft, bottomright;
1607 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1608 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1609 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1610 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1611 dp->ce1 = upperleft.east_west;
1612 dp->ce2 = bottomright.east_west;
1613 dp->cn1 = bottomright.north_south;
1614 dp->cn2 = upperleft.north_south;
1617 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1621 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1622 * Here a simple traffic like light colour system is used:
1623 * . slow points are red
1624 * . average is yellow
1625 * . fast points are green
1627 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1630 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1631 if ( average_speed > 0 ) {
1632 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1633 if ( rv < low_speed )
1634 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1635 else if ( rv > high_speed )
1636 return VIK_TRW_LAYER_TRACK_GC_FAST;
1638 return VIK_TRW_LAYER_TRACK_GC_AVER;
1641 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1644 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1646 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1647 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1648 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1649 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1653 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1655 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1657 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1658 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1660 // Fallback if parse failure
1661 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1663 g_free ( label_markup );
1665 gint label_x, label_y;
1667 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1669 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1670 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1674 * distance_in_preferred_units:
1675 * @dist: The source distance in standard SI Units (i.e. metres)
1677 * TODO: This is a generic function that could be moved into globals.c or utils.c
1679 * Probably best used if you have a only few conversions to perform.
1680 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1681 * since it will be doing the preference check on each call
1683 * Returns: The distance in the units as specified by the preferences
1685 static gdouble distance_in_preferred_units ( gdouble dist )
1688 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1689 switch (dist_units) {
1690 case VIK_UNITS_DISTANCE_MILES:
1691 mydist = VIK_METERS_TO_MILES(dist);
1693 // VIK_UNITS_DISTANCE_KILOMETRES:
1695 mydist = dist/1000.0;
1702 * trw_layer_draw_dist_labels:
1704 * Draw a few labels along a track at nicely seperated distances
1705 * This might slow things down if there's many tracks being displayed with this on.
1707 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1709 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1710 25.0, 40.0, 50.0, 75.0, 100.0,
1711 150.0, 200.0, 250.0, 500.0, 1000.0};
1713 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1715 // Convert to specified unit to find the friendly breakdown value
1716 dist = distance_in_preferred_units ( dist );
1720 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1721 if ( chunksd[i] > dist ) {
1723 dist = chunksd[index];
1728 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1730 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1731 gdouble dist_i = dist * i;
1733 // Convert distance back into metres for use in finding a trackpoint
1734 switch (dist_units) {
1735 case VIK_UNITS_DISTANCE_MILES:
1736 dist_i = VIK_MILES_TO_METERS(dist_i);
1738 // VIK_UNITS_DISTANCE_KILOMETRES:
1740 dist_i = dist_i*1000.0;
1744 gdouble dist_current = 0.0;
1745 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1746 gdouble dist_next = 0.0;
1747 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1749 gdouble dist_between_tps = fabs (dist_next - dist_current);
1750 gdouble ratio = 0.0;
1751 // Prevent division by 0 errors
1752 if ( dist_between_tps > 0.0 )
1753 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1755 if ( tp_current && tp_next ) {
1756 // Construct the name based on the distance value
1759 switch (dist_units) {
1760 case VIK_UNITS_DISTANCE_MILES:
1761 units = g_strdup ( _("miles") );
1763 // VIK_UNITS_DISTANCE_KILOMETRES:
1765 units = g_strdup ( _("km") );
1769 // Convert for display
1770 dist_i = distance_in_preferred_units ( dist_i );
1772 // Make the precision of the output related to the unit size.
1774 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1775 else if ( index == 1 )
1776 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1778 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1781 struct LatLon ll_current, ll_next;
1782 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1783 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1785 // positional interpolation
1786 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1787 // but should be good enough over the small scale that I anticipate usage on
1788 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1789 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1791 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1794 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1795 fgcolour = gdk_color_to_string ( &(trk->color) );
1797 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1799 // if highlight mode on, then colour the background in the highlight colour
1801 if ( drawing_highlight )
1802 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1804 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1806 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1808 g_free ( fgcolour );
1809 g_free ( bgcolour );
1816 * trw_layer_draw_track_name_labels:
1818 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1820 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1823 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1824 fgcolour = gdk_color_to_string ( &(trk->color) );
1826 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1828 // if highlight mode on, then colour the background in the highlight colour
1830 if ( drawing_highlight )
1831 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1833 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1835 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1837 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1838 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1839 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1840 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1841 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1842 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1844 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1846 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1849 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1850 // No other labels to draw
1853 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1856 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1859 VikCoord begin_coord = tp_begin->coord;
1860 VikCoord end_coord = tp_end->coord;
1862 gboolean done_start_end = FALSE;
1864 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1865 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1867 // This number can be configured via the settings if you really want to change it
1868 gdouble distance_diff;
1869 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1870 distance_diff = 100.0; // Metres
1872 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1873 // Start and end 'close' together so only draw one label at an average location
1874 gint x1, x2, y1, y2;
1875 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1876 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1878 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1880 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1881 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1884 done_start_end = TRUE;
1888 if ( ! done_start_end ) {
1889 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1890 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1891 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1892 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1893 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1894 g_free ( name_start );
1896 // Don't draw end label if this is the one being created
1897 if ( trk != dp->vtl->current_track ) {
1898 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1899 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1900 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1901 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1902 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1903 g_free ( name_end );
1908 g_free ( fgcolour );
1909 g_free ( bgcolour );
1913 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1915 if ( ! track->visible )
1918 /* TODO: this function is a mess, get rid of any redundancy */
1919 GList *list = track->trackpoints;
1921 gboolean useoldvals = TRUE;
1923 gboolean drawpoints;
1925 gboolean drawelevation;
1926 gdouble min_alt, max_alt, alt_diff = 0;
1928 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1929 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1932 if ( dp->vtl->drawelevation )
1934 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1935 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1936 alt_diff = max_alt - min_alt;
1939 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1940 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1941 trw_layer_draw_track ( id, track, dp, TRUE );
1943 if ( draw_track_outline )
1944 drawpoints = drawstops = FALSE;
1946 drawpoints = dp->vtl->drawpoints;
1947 drawstops = dp->vtl->drawstops;
1950 gboolean drawing_highlight = FALSE;
1951 /* Current track - used for creation */
1952 if ( track == dp->vtl->current_track )
1953 main_gc = dp->vtl->current_track_gc;
1955 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1956 /* Draw all tracks of the layer in special colour */
1957 /* if track is member of selected layer or is the current selected track
1958 then draw in the highlight colour.
1959 NB this supercedes the drawmode */
1960 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1961 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1962 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1963 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1964 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1965 drawing_highlight = TRUE;
1968 if ( !drawing_highlight ) {
1969 // Still need to figure out the gc according to the drawing mode:
1970 switch ( dp->vtl->drawmode ) {
1971 case DRAWMODE_BY_TRACK:
1972 if ( dp->vtl->track_1color_gc )
1973 g_object_unref ( dp->vtl->track_1color_gc );
1974 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1975 main_gc = dp->vtl->track_1color_gc;
1978 // Mostly for DRAWMODE_ALL_SAME_COLOR
1979 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1980 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1987 int x, y, oldx, oldy;
1988 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1990 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1992 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1994 // Draw the first point as something a bit different from the normal points
1995 // ATM it's slightly bigger and a triangle
1997 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1998 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2004 gdouble average_speed = 0.0;
2005 gdouble low_speed = 0.0;
2006 gdouble high_speed = 0.0;
2007 // If necessary calculate these values - which is done only once per track redraw
2008 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2009 // the percentage factor away from the average speed determines transistions between the levels
2010 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2011 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2012 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2015 while ((list = g_list_next(list)))
2017 tp = VIK_TRACKPOINT(list->data);
2018 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2020 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2021 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2022 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2023 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2024 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2026 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2029 * If points are the same in display coordinates, don't draw.
2031 if ( useoldvals && x == oldx && y == oldy )
2033 // Still need to process points to ensure 'stops' are drawn if required
2034 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2035 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2036 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 );
2041 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2042 if ( drawpoints || dp->vtl->drawlines ) {
2043 // setup main_gc for both point and line drawing
2044 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2045 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 ) );
2049 if ( drawpoints && ! draw_track_outline )
2054 * The concept of drawing stops is that a trackpoint
2055 * that is if the next trackpoint has a timestamp far into
2056 * the future, we draw a circle of 6x trackpoint size,
2057 * instead of a rectangle of 2x trackpoint size.
2058 * This is drawn first so the trackpoint will be drawn on top
2061 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2062 /* Stop point. Draw 6x circle. Always in redish colour */
2063 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 );
2065 /* Regular point - draw 2x square. */
2066 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2069 /* Final point - draw 4x circle. */
2070 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 );
2073 if ((!tp->newsegment) && (dp->vtl->drawlines))
2076 /* UTM only: zone check */
2077 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2078 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2081 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2083 if ( draw_track_outline ) {
2084 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2088 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2090 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2092 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2097 tmp[1].y = oldy-FIXALTITUDE(list->data);
2099 tmp[2].y = y-FIXALTITUDE(list->next->data);
2104 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2105 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2107 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2108 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2110 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2115 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2116 // Draw an arrow at the mid point to show the direction of the track
2117 // Code is a rework from vikwindow::draw_ruler()
2118 gint midx = (oldx + x) / 2;
2119 gint midy = (oldy + y) / 2;
2121 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2122 // Avoid divide by zero and ensure at least 1 pixel big
2124 gdouble dx = (oldx - midx) / len;
2125 gdouble dy = (oldy - midy) / len;
2126 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2127 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2137 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2139 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2140 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2142 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2144 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2145 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 ));
2149 * If points are the same in display coordinates, don't draw.
2151 if ( x != oldx || y != oldy )
2153 if ( draw_track_outline )
2154 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2156 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2162 * If points are the same in display coordinates, don't draw.
2164 if ( x != oldx && y != oldy )
2166 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2167 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2175 // Labels drawn after the trackpoints, so the labels are on top
2176 if ( dp->vtl->track_draw_labels ) {
2177 if ( track->max_number_dist_labels > 0 ) {
2178 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2181 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2182 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2188 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2190 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2191 trw_layer_draw_track ( id, track, dp, FALSE );
2195 static void cached_pixbuf_free ( CachedPixbuf *cp )
2197 g_object_unref ( G_OBJECT(cp->pixbuf) );
2198 g_free ( cp->image );
2201 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2203 return strcmp ( cp->image, name );
2206 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2209 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2210 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2211 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2214 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2216 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2218 if ( wp->image && dp->vtl->drawimages )
2220 GdkPixbuf *pixbuf = NULL;
2223 if ( dp->vtl->image_alpha == 0)
2226 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2228 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2231 gchar *image = wp->image;
2232 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2233 if ( ! regularthumb )
2235 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2236 image = "\x12\x00"; /* this shouldn't occur naturally. */
2240 CachedPixbuf *cp = NULL;
2241 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2242 if ( dp->vtl->image_size == 128 )
2243 cp->pixbuf = regularthumb;
2246 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2247 g_assert ( cp->pixbuf );
2248 g_object_unref ( G_OBJECT(regularthumb) );
2250 cp->image = g_strdup ( image );
2252 /* needed so 'click picture' tool knows how big the pic is; we don't
2253 * store it in cp because they may have been freed already. */
2254 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2255 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2257 g_queue_push_head ( dp->vtl->image_cache, cp );
2258 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2259 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2261 pixbuf = cp->pixbuf;
2265 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2271 w = gdk_pixbuf_get_width ( pixbuf );
2272 h = gdk_pixbuf_get_height ( pixbuf );
2274 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2276 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2277 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2278 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2279 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2280 // Highlighted - so draw a little border around the chosen one
2281 // single line seems a little weak so draw 2 of them
2282 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2283 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2284 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2285 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2288 if ( dp->vtl->image_alpha == 255 )
2289 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2291 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2293 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2297 // Draw appropriate symbol - either symbol image or simple types
2298 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2299 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 );
2301 else if ( wp == dp->vtl->current_wp ) {
2302 switch ( dp->vtl->wp_symbol ) {
2303 case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2304 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;
2305 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;
2306 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 );
2307 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 );
2311 switch ( dp->vtl->wp_symbol ) {
2312 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;
2313 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;
2314 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;
2315 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 );
2316 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;
2320 if ( dp->vtl->drawlabels )
2322 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2323 gint label_x, label_y;
2325 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2327 // Could this stored in the waypoint rather than recreating each pass?
2328 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2330 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2331 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2333 // Fallback if parse failure
2334 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2336 g_free ( wp_label_markup );
2338 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2339 label_x = x - width/2;
2340 if ( wp->symbol_pixbuf )
2341 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2343 label_y = y - dp->vtl->wp_size - height - 2;
2345 /* if highlight mode on, then draw background text in highlight colour */
2346 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2347 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2348 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2349 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2350 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2352 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2355 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2357 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2362 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2364 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2365 trw_layer_draw_waypoint ( id, wp, dp );
2369 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2371 static struct DrawingParams dp;
2372 g_assert ( l != NULL );
2374 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2376 if ( l->tracks_visible )
2377 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2379 if ( l->routes_visible )
2380 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2382 if (l->waypoints_visible)
2383 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2386 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2389 if ( vtl->track_bg_gc )
2391 g_object_unref ( vtl->track_bg_gc );
2392 vtl->track_bg_gc = NULL;
2394 if ( vtl->track_1color_gc )
2396 g_object_unref ( vtl->track_1color_gc );
2397 vtl->track_1color_gc = NULL;
2399 if ( vtl->current_track_gc )
2401 g_object_unref ( vtl->current_track_gc );
2402 vtl->current_track_gc = NULL;
2404 if ( vtl->current_track_newpoint_gc )
2406 g_object_unref ( vtl->current_track_newpoint_gc );
2407 vtl->current_track_newpoint_gc = NULL;
2410 if ( ! vtl->track_gc )
2412 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2413 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2414 g_array_free ( vtl->track_gc, TRUE );
2415 vtl->track_gc = NULL;
2418 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2420 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2421 gint width = vtl->line_thickness;
2423 if ( vtl->track_gc )
2424 trw_layer_free_track_gcs ( vtl );
2426 if ( vtl->track_bg_gc )
2427 g_object_unref ( vtl->track_bg_gc );
2428 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2430 // Ensure new track drawing heeds line thickness setting
2431 // however always have a minium of 2, as 1 pixel is really narrow
2432 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2434 if ( vtl->current_track_gc )
2435 g_object_unref ( vtl->current_track_gc );
2436 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2437 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2439 // 'newpoint' gc is exactly the same as the current track gc
2440 if ( vtl->current_track_newpoint_gc )
2441 g_object_unref ( vtl->current_track_newpoint_gc );
2442 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2443 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2445 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2447 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2448 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2450 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2451 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2452 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2454 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2456 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2459 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2461 VikTrwLayer *rv = trw_layer_new1 ( vp );
2462 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2464 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2465 /* early exit, as the rest is GUI related */
2469 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2470 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2472 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2473 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2475 trw_layer_new_track_gcs ( rv, vp );
2477 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2478 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2479 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2480 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2482 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2484 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2489 #define SMALL_ICON_SIZE 18
2491 * Can accept a null symbol, and may return null value
2493 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2495 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2496 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2497 // So needing a small icon for the treeview may need some resizing:
2498 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2499 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2503 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2505 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2507 GdkPixbuf *pixbuf = NULL;
2509 if ( track->has_color ) {
2510 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2511 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2512 // Here is some magic found to do the conversion
2513 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2514 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2515 ((track->color.green & 0xff00) << 8) |
2516 (track->color.blue & 0xff00);
2518 gdk_pixbuf_fill ( pixbuf, pixel );
2521 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 );
2524 g_object_unref (pixbuf);
2526 *new_iter = *((GtkTreeIter *) pass_along[1]);
2527 if ( track->is_route )
2528 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2530 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2532 if ( ! track->visible )
2533 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2536 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2538 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2540 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 );
2542 *new_iter = *((GtkTreeIter *) pass_along[1]);
2543 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2545 if ( ! wp->visible )
2546 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2549 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2551 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2554 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2556 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2559 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2561 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2564 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2567 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2569 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2570 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2572 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2574 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2577 if ( g_hash_table_size (vtl->routes) > 0 ) {
2578 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2580 pass_along[0] = &(vtl->routes_iter);
2581 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2583 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2585 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2588 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2589 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2591 pass_along[0] = &(vtl->waypoints_iter);
2592 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2594 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2596 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2601 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2605 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2606 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2607 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2608 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2610 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2612 return (t->visible ^= 1);
2616 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2618 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2620 return (t->visible ^= 1);
2624 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2626 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2628 return (t->visible ^= 1);
2637 * Return a property about tracks for this layer
2639 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2641 return vtl->line_thickness;
2644 // Structure to hold multiple track information for a layer
2653 * Build up layer multiple track information via updating the tooltip_tracks structure
2655 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2657 tt->length = tt->length + vik_track_get_length (tr);
2659 // Ensure times are available
2660 if ( tr->trackpoints &&
2661 vik_track_get_tp_first(tr)->has_timestamp &&
2662 vik_track_get_tp_last(tr)->has_timestamp ) {
2665 t1 = vik_track_get_tp_first(tr)->timestamp;
2666 t2 = vik_track_get_tp_last(tr)->timestamp;
2668 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2669 // Hence initialize to the first 'proper' value
2670 if ( tt->start_time == 0 )
2671 tt->start_time = t1;
2672 if ( tt->end_time == 0 )
2675 // Update find the earliest / last times
2676 if ( t1 < tt->start_time )
2677 tt->start_time = t1;
2678 if ( t2 > tt->end_time )
2681 // Keep track of total time
2682 // there maybe gaps within a track (eg segments)
2683 // but this should be generally good enough for a simple indicator
2684 tt->duration = tt->duration + (int)(t2-t1);
2689 * Generate tooltip text for the layer.
2690 * This is relatively complicated as it considers information for
2691 * no tracks, a single track or multiple tracks
2692 * (which may or may not have timing information)
2694 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2705 static gchar tmp_buf[128];
2708 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2710 // Safety check - I think these should always be valid
2711 if ( vtl->tracks && vtl->waypoints ) {
2712 tooltip_tracks tt = { 0.0, 0, 0 };
2713 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2715 GDate* gdate_start = g_date_new ();
2716 g_date_set_time_t (gdate_start, tt.start_time);
2718 GDate* gdate_end = g_date_new ();
2719 g_date_set_time_t (gdate_end, tt.end_time);
2721 if ( g_date_compare (gdate_start, gdate_end) ) {
2722 // Dates differ so print range on separate line
2723 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2724 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2725 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2728 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2729 if ( tt.start_time != 0 )
2730 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2734 if ( tt.length > 0.0 ) {
2735 gdouble len_in_units;
2737 // Setup info dependent on distance units
2738 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2739 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2740 len_in_units = VIK_METERS_TO_MILES(tt.length);
2743 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2744 len_in_units = tt.length/1000.0;
2747 // Timing information if available
2749 if ( tt.duration > 0 ) {
2750 g_snprintf (tbuf1, sizeof(tbuf1),
2751 _(" in %d:%02d hrs:mins"),
2752 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2754 g_snprintf (tbuf2, sizeof(tbuf2),
2755 _("\n%sTotal Length %.1f %s%s"),
2756 tbuf3, len_in_units, tbuf4, tbuf1);
2759 // Put together all the elements to form compact tooltip text
2760 g_snprintf (tmp_buf, sizeof(tmp_buf),
2761 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2762 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2764 g_date_free (gdate_start);
2765 g_date_free (gdate_end);
2772 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2776 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2778 // Very simple tooltip - may expand detail in the future...
2779 static gchar tmp_buf[32];
2780 g_snprintf (tmp_buf, sizeof(tmp_buf),
2782 g_hash_table_size (l->tracks));
2786 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2788 // Very simple tooltip - may expand detail in the future...
2789 static gchar tmp_buf[32];
2790 g_snprintf (tmp_buf, sizeof(tmp_buf),
2792 g_hash_table_size (l->routes));
2797 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2798 // Same tooltip for a route
2799 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2802 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2803 tr = g_hash_table_lookup ( l->tracks, sublayer );
2805 tr = g_hash_table_lookup ( l->routes, sublayer );
2808 // Could be a better way of handling strings - but this works...
2809 gchar time_buf1[20];
2810 gchar time_buf2[20];
2811 time_buf1[0] = '\0';
2812 time_buf2[0] = '\0';
2813 static gchar tmp_buf[100];
2814 // Compact info: Short date eg (11/20/99), duration and length
2815 // Hopefully these are the things that are most useful and so promoted into the tooltip
2816 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2817 // %x The preferred date representation for the current locale without the time.
2818 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2819 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2820 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2822 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2825 // Get length and consider the appropriate distance units
2826 gdouble tr_len = vik_track_get_length(tr);
2827 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2828 switch (dist_units) {
2829 case VIK_UNITS_DISTANCE_KILOMETRES:
2830 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2832 case VIK_UNITS_DISTANCE_MILES:
2833 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2842 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2844 // Very simple tooltip - may expand detail in the future...
2845 static gchar tmp_buf[32];
2846 g_snprintf (tmp_buf, sizeof(tmp_buf),
2848 g_hash_table_size (l->waypoints));
2852 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2854 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2855 // NB It's OK to return NULL
2860 return w->description;
2869 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2872 * set_statusbar_msg_info_trkpt:
2874 * Function to show track point information on the statusbar
2875 * Items displayed is controlled by the settings format code
2877 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2879 gchar *statusbar_format_code = NULL;
2880 gboolean need2free = FALSE;
2881 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2882 // Otherwise use default
2883 statusbar_format_code = g_strdup ( "KEATDN" );
2887 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2888 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2892 g_free ( statusbar_format_code );
2896 * Function to show basic waypoint information on the statusbar
2898 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2901 switch (a_vik_get_units_height ()) {
2902 case VIK_UNITS_HEIGHT_FEET:
2903 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2906 //VIK_UNITS_HEIGHT_METRES:
2907 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2911 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2912 // one can easily use the current pointer position to see this if needed
2913 gchar *lat = NULL, *lon = NULL;
2914 static struct LatLon ll;
2915 vik_coord_to_latlon (&(wpt->coord), &ll);
2916 a_coords_latlon_to_string ( &ll, &lat, &lon );
2918 // Combine parts to make overall message
2921 // Add comment if available
2922 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2924 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2925 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2932 * General layer selection function, find out which bit is selected and take appropriate action
2934 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2937 l->current_wp = NULL;
2938 l->current_wp_id = NULL;
2939 trw_layer_cancel_current_tp ( l, FALSE );
2942 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2946 case VIK_TREEVIEW_TYPE_LAYER:
2948 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2949 /* Mark for redraw */
2954 case VIK_TREEVIEW_TYPE_SUBLAYER:
2958 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2960 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2961 /* Mark for redraw */
2965 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2967 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2968 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2969 /* Mark for redraw */
2973 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2975 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2976 /* Mark for redraw */
2980 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2982 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2983 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2984 /* Mark for redraw */
2988 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2990 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2991 /* Mark for redraw */
2995 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2997 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2999 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3000 // Show some waypoint info
3001 set_statusbar_msg_info_wpt ( l, wpt );
3002 /* Mark for redraw */
3009 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3018 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3023 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3028 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3033 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3035 return l->waypoints;
3038 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3040 return vtl->tracks_iters;
3043 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3045 return vtl->routes_iters;
3048 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3050 return vtl->waypoints;
3053 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3055 return ! ( g_hash_table_size ( vtl->tracks ) ||
3056 g_hash_table_size ( vtl->routes ) ||
3057 g_hash_table_size ( vtl->waypoints ) );
3060 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3062 return vtl->tracks_visible;
3065 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3067 return vtl->routes_visible;
3070 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3072 return vtl->waypoints_visible;
3076 * ATM use a case sensitive find
3077 * Finds the first one
3079 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3081 if ( wp && wp->name )
3082 if ( ! strcmp ( wp->name, name ) )
3088 * Get waypoint by name - not guaranteed to be unique
3089 * Finds the first one
3091 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3093 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3097 * ATM use a case sensitive find
3098 * Finds the first one
3100 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3102 if ( trk && trk->name )
3103 if ( ! strcmp ( trk->name, name ) )
3109 * Get track by name - not guaranteed to be unique
3110 * Finds the first one
3112 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3114 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3118 * Get route by name - not guaranteed to be unique
3119 * Finds the first one
3121 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3123 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3126 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3128 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3129 maxmin[0].lat = trk->bbox.north;
3130 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3131 maxmin[1].lat = trk->bbox.south;
3132 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3133 maxmin[0].lon = trk->bbox.east;
3134 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3135 maxmin[1].lon = trk->bbox.west;
3138 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3140 // Continually reuse maxmin to find the latest maximum and minimum values
3141 // First set to waypoints bounds
3142 maxmin[0].lat = vtl->waypoints_bbox.north;
3143 maxmin[1].lat = vtl->waypoints_bbox.south;
3144 maxmin[0].lon = vtl->waypoints_bbox.east;
3145 maxmin[1].lon = vtl->waypoints_bbox.west;
3146 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3147 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3150 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3152 /* 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... */
3153 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3154 trw_layer_find_maxmin (vtl, maxmin);
3155 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3159 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3160 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3165 static void trw_layer_centerize ( menu_array_layer values )
3167 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3169 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3170 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3172 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3175 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3177 /* First set the center [in case previously viewing from elsewhere] */
3178 /* Then loop through zoom levels until provided positions are in view */
3179 /* This method is not particularly fast - but should work well enough */
3180 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3182 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3183 vik_viewport_set_center_coord ( vvp, &coord );
3185 /* Convert into definite 'smallest' and 'largest' positions */
3186 struct LatLon minmin;
3187 if ( maxmin[0].lat < maxmin[1].lat )
3188 minmin.lat = maxmin[0].lat;
3190 minmin.lat = maxmin[1].lat;
3192 struct LatLon maxmax;
3193 if ( maxmin[0].lon > maxmin[1].lon )
3194 maxmax.lon = maxmin[0].lon;
3196 maxmax.lon = maxmin[1].lon;
3198 /* Never zoom in too far - generally not that useful, as too close ! */
3199 /* Always recalculate the 'best' zoom level */
3201 vik_viewport_set_zoom ( vvp, zoom );
3203 gdouble min_lat, max_lat, min_lon, max_lon;
3204 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3205 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3206 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3207 /* NB I think the logic used in this test to determine if the bounds is within view
3208 fails if track goes across 180 degrees longitude.
3209 Hopefully that situation is not too common...
3210 Mind you viking doesn't really do edge locations to well anyway */
3211 if ( min_lat < minmin.lat &&
3212 max_lat > minmin.lat &&
3213 min_lon < maxmax.lon &&
3214 max_lon > maxmax.lon )
3215 /* Found within zoom level */
3220 vik_viewport_set_zoom ( vvp, zoom );
3224 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3226 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3227 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3228 trw_layer_find_maxmin (vtl, maxmin);
3229 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3232 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3237 static void trw_layer_auto_view ( menu_array_layer values )
3239 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3240 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3241 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3242 vik_layers_panel_emit_update ( vlp );
3245 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3248 static void trw_layer_export_gpspoint ( menu_array_layer values )
3250 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3252 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3254 g_free ( auto_save_name );
3257 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3259 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3261 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3263 g_free ( auto_save_name );
3266 static void trw_layer_export_gpx ( menu_array_layer values )
3268 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3270 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3272 g_free ( auto_save_name );
3275 static void trw_layer_export_kml ( menu_array_layer values )
3277 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3279 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3281 g_free ( auto_save_name );
3284 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3286 gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3287 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3290 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3292 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3295 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3297 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3300 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3302 menu_array_layer data;
3303 data[MA_VTL] = values[MA_VTL];
3304 data[MA_VLP] = values[MA_VLP];
3306 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3308 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3309 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3311 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3313 if ( !trk || !trk->name )
3316 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3318 gchar *label = NULL;
3319 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3320 label = _("Export Route as GPX");
3322 label = _("Export Track as GPX");
3323 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3325 g_free ( auto_save_name );
3328 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3330 wpu_udata *user_data = udata;
3331 if ( wp == user_data->wp ) {
3332 user_data->uuid = id;
3338 static void trw_layer_goto_wp ( menu_array_layer values )
3340 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3341 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3342 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3343 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3344 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3346 GTK_RESPONSE_REJECT,
3348 GTK_RESPONSE_ACCEPT,
3351 GtkWidget *label, *entry;
3352 label = gtk_label_new(_("Waypoint Name:"));
3353 entry = gtk_entry_new();
3355 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3356 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3357 gtk_widget_show_all ( label );
3358 gtk_widget_show_all ( entry );
3360 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3362 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3364 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3365 // Find *first* wp with the given name
3366 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3369 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3372 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3373 vik_layers_panel_emit_update ( vlp );
3375 // Find and select on the side panel
3380 // Hmmm, want key of it
3381 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3383 if ( wpf && udata.uuid ) {
3384 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3385 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3394 gtk_widget_destroy ( dia );
3397 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3399 gchar *default_name = highest_wp_number_get(vtl);
3400 VikWaypoint *wp = vik_waypoint_new();
3401 gchar *returned_name;
3403 wp->coord = *def_coord;
3405 // Attempt to auto set height if DEM data is available
3406 vik_waypoint_apply_dem_data ( wp, TRUE );
3408 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3410 if ( returned_name )
3413 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3414 g_free (default_name);
3415 g_free (returned_name);
3418 g_free (default_name);
3419 vik_waypoint_free(wp);
3423 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3425 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3426 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3427 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3428 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3429 VikViewport *vvp = vik_window_viewport(vw);
3431 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3432 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3433 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3434 trw_layer_calculate_bounds_waypoints ( vtl );
3435 vik_layers_panel_emit_update ( vlp );
3438 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3440 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3441 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3442 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3444 trw_layer_find_maxmin (vtl, maxmin);
3445 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3446 trw_layer_calculate_bounds_waypoints ( vtl );
3447 vik_layers_panel_emit_update ( vlp );
3450 #ifdef VIK_CONFIG_GEOTAG
3451 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3453 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3455 // Update directly - not changing the mtime
3456 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3459 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3461 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3464 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3468 * Use code in separate file for this feature as reasonably complex
3470 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3472 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3473 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3474 // Unset so can be reverified later if necessary
3475 vtl->has_verified_thumbnails = FALSE;
3477 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3483 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3485 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3486 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3488 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3494 static void trw_layer_geotagging ( menu_array_layer values )
3496 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3497 // Unset so can be reverified later if necessary
3498 vtl->has_verified_thumbnails = FALSE;
3500 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3507 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3509 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3511 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3512 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3513 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3514 VikViewport *vvp = vik_window_viewport(vw);
3516 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3520 * Acquire into this TRW Layer straight from GPS Device
3522 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3524 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3525 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3529 * Acquire into this TRW Layer from Directions
3531 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3533 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3537 * Acquire into this TRW Layer from an entered URL
3539 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3541 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3542 trw_layer_acquire ( values, &vik_datasource_url_interface );
3545 #ifdef VIK_CONFIG_OPENSTREETMAP
3547 * Acquire into this TRW Layer from OSM
3549 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3551 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3555 * Acquire into this TRW Layer from OSM for 'My' Traces
3557 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3559 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3563 #ifdef VIK_CONFIG_GEOCACHES
3565 * Acquire into this TRW Layer from Geocaching.com
3567 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3569 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3573 #ifdef VIK_CONFIG_GEOTAG
3575 * Acquire into this TRW Layer from images
3577 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3579 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3581 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3582 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3584 // Reverify thumbnails as they may have changed
3585 vtl->has_verified_thumbnails = FALSE;
3586 trw_layer_verify_thumbnails ( vtl, NULL );
3590 static void trw_layer_gps_upload ( menu_array_layer values )
3592 menu_array_sublayer data;
3594 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3596 data[MA_VTL] = values[MA_VTL];
3597 data[MA_VLP] = values[MA_VLP];
3599 trw_layer_gps_upload_any ( data );
3603 * If pass_along[3] is defined that this will upload just that track
3605 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3607 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3608 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3610 // May not actually get a track here as values[2&3] can be null
3611 VikTrack *track = NULL;
3612 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3613 gboolean xfer_all = FALSE;
3615 if ( values[MA_SUBTYPE] ) {
3617 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3618 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3621 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3622 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3625 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3628 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3632 else if ( !values[MA_CONFIRM] )
3633 xfer_all = TRUE; // i.e. whole layer
3635 if (track && !track->visible) {
3636 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3640 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3641 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3642 GTK_DIALOG_DESTROY_WITH_PARENT,
3644 GTK_RESPONSE_ACCEPT,
3646 GTK_RESPONSE_REJECT,
3649 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3650 GtkWidget *response_w = NULL;
3651 #if GTK_CHECK_VERSION (2, 20, 0)
3652 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3656 gtk_widget_grab_focus ( response_w );
3658 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3660 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3661 datasource_gps_clean_up ( dgs );
3662 gtk_widget_destroy ( dialog );
3666 // Get info from reused datasource dialog widgets
3667 gchar* protocol = datasource_gps_get_protocol ( dgs );
3668 gchar* port = datasource_gps_get_descriptor ( dgs );
3669 // NB don't free the above strings as they're references to values held elsewhere
3670 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3671 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3672 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3673 gboolean turn_off = datasource_gps_get_off ( dgs );
3675 gtk_widget_destroy ( dialog );
3677 // When called from the viewport - work the corresponding layerspanel:
3679 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3682 // Apply settings to transfer to the GPS device
3689 vik_layers_panel_get_viewport (vlp),
3698 * Acquire into this TRW Layer from any GPS Babel supported file
3700 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3702 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3703 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3704 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3705 VikViewport *vvp = vik_window_viewport(vw);
3707 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3710 static void trw_layer_new_wp ( menu_array_layer values )
3712 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3713 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3714 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3715 instead return true if you want to update. */
3716 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 ) {
3717 trw_layer_calculate_bounds_waypoints ( vtl );
3718 vik_layers_panel_emit_update ( vlp );
3722 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3724 vtl->current_track = vik_track_new();
3725 vik_track_set_defaults ( vtl->current_track );
3726 vtl->current_track->visible = TRUE;
3727 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3728 // Create track with the preferred colour from the layer properties
3729 vtl->current_track->color = vtl->track_color;
3731 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3732 vtl->current_track->has_color = TRUE;
3733 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3736 static void trw_layer_new_track ( menu_array_layer values )
3738 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3740 if ( ! vtl->current_track ) {
3741 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3742 new_track_create_common ( vtl, name );
3745 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3749 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3751 vtl->current_track = vik_track_new();
3752 vik_track_set_defaults ( vtl->current_track );
3753 vtl->current_track->visible = TRUE;
3754 vtl->current_track->is_route = TRUE;
3755 // By default make all routes red
3756 vtl->current_track->has_color = TRUE;
3757 gdk_color_parse ( "red", &vtl->current_track->color );
3758 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3761 static void trw_layer_new_route ( menu_array_layer values )
3763 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3765 if ( ! vtl->current_track ) {
3766 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3767 new_route_create_common ( vtl, name );
3769 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3773 static void trw_layer_auto_routes_view ( menu_array_layer values )
3775 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3776 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3778 if ( g_hash_table_size (vtl->routes) > 0 ) {
3779 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3780 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3781 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3782 vik_layers_panel_emit_update ( vlp );
3787 static void trw_layer_finish_track ( menu_array_layer values )
3789 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3790 vtl->current_track = NULL;
3791 vik_layer_emit_update ( VIK_LAYER(vtl) );
3794 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3796 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3797 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3799 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3800 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3801 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3802 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3803 vik_layers_panel_emit_update ( vlp );
3807 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3809 /* NB do not care if wp is visible or not */
3810 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3813 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3815 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3816 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3818 /* Only 1 waypoint - jump straight to it */
3819 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3820 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3821 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3823 /* If at least 2 waypoints - find center and then zoom to fit */
3824 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3826 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3827 maxmin[0].lat = vtl->waypoints_bbox.north;
3828 maxmin[1].lat = vtl->waypoints_bbox.south;
3829 maxmin[0].lon = vtl->waypoints_bbox.east;
3830 maxmin[1].lon = vtl->waypoints_bbox.west;
3831 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3834 vik_layers_panel_emit_update ( vlp );
3837 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3839 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3842 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3844 if ( values[MA_MISC] ) {
3845 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3846 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3850 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3852 static menu_array_layer pass_along;
3854 GtkWidget *export_submenu;
3855 pass_along[MA_VTL] = vtl;
3856 pass_along[MA_VLP] = vlp;
3858 item = gtk_menu_item_new();
3859 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3860 gtk_widget_show ( item );
3862 if ( vtl->current_track ) {
3863 if ( vtl->current_track->is_route )
3864 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3866 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3868 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3869 gtk_widget_show ( item );
3872 item = gtk_menu_item_new ();
3873 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3874 gtk_widget_show ( item );
3877 /* Now with icons */
3878 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3879 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3880 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3881 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3882 gtk_widget_show ( item );
3884 GtkWidget *view_submenu = gtk_menu_new();
3885 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3887 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3888 gtk_widget_show ( item );
3889 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3891 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3893 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3894 gtk_widget_show ( item );
3896 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3898 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3899 gtk_widget_show ( item );
3901 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3903 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3904 gtk_widget_show ( item );
3906 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3907 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3909 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3910 gtk_widget_show ( item );
3912 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3913 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3914 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3915 gtk_widget_show ( item );
3917 export_submenu = gtk_menu_new ();
3918 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3919 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3920 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3921 gtk_widget_show ( item );
3922 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3924 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3925 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3926 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3927 gtk_widget_show ( item );
3929 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3930 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3931 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3932 gtk_widget_show ( item );
3934 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3936 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3937 gtk_widget_show ( item );
3939 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3941 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3942 gtk_widget_show ( item );
3944 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
3945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
3946 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3947 gtk_widget_show ( item );
3949 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3950 item = gtk_menu_item_new_with_mnemonic ( external1 );
3951 g_free ( external1 );
3952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3953 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3954 gtk_widget_show ( item );
3956 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
3957 item = gtk_menu_item_new_with_mnemonic ( external2 );
3958 g_free ( external2 );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3960 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3961 gtk_widget_show ( item );
3963 GtkWidget *new_submenu = gtk_menu_new();
3964 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3965 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3966 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3967 gtk_widget_show(item);
3968 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3970 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3973 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3974 gtk_widget_show ( item );
3976 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3979 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3980 gtk_widget_show ( item );
3981 // Make it available only when a new track *not* already in progress
3982 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3984 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3985 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3988 gtk_widget_show ( item );
3989 // Make it available only when a new track *not* already in progress
3990 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3992 #ifdef VIK_CONFIG_GEOTAG
3993 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3995 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3996 gtk_widget_show ( item );
3999 GtkWidget *acquire_submenu = gtk_menu_new ();
4000 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4002 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4003 gtk_widget_show ( item );
4004 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4006 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4007 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4008 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4009 gtk_widget_show ( item );
4011 /* FIXME: only add menu when at least a routing engine has support for Directions */
4012 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4014 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4015 gtk_widget_show ( item );
4017 #ifdef VIK_CONFIG_OPENSTREETMAP
4018 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4020 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4021 gtk_widget_show ( item );
4023 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4025 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4026 gtk_widget_show ( item );
4029 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4031 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4032 gtk_widget_show ( item );
4034 #ifdef VIK_CONFIG_GEONAMES
4035 GtkWidget *wikipedia_submenu = gtk_menu_new();
4036 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4037 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4038 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4039 gtk_widget_show(item);
4040 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4042 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4043 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4045 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4046 gtk_widget_show ( item );
4048 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4049 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4051 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4052 gtk_widget_show ( item );
4055 #ifdef VIK_CONFIG_GEOCACHES
4056 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4057 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4058 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4059 gtk_widget_show ( item );
4062 #ifdef VIK_CONFIG_GEOTAG
4063 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4065 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4066 gtk_widget_show ( item );
4069 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4071 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4072 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4073 gtk_widget_show ( item );
4075 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4077 GtkWidget *upload_submenu = gtk_menu_new ();
4078 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4079 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4080 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4081 gtk_widget_show ( item );
4082 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4084 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4087 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4088 gtk_widget_show ( item );
4090 #ifdef VIK_CONFIG_OPENSTREETMAP
4091 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4092 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4094 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4095 gtk_widget_show ( item );
4098 GtkWidget *delete_submenu = gtk_menu_new ();
4099 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4101 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4102 gtk_widget_show ( item );
4103 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4105 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4108 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4109 gtk_widget_show ( item );
4111 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4112 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4114 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4115 gtk_widget_show ( item );
4117 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4118 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4120 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4121 gtk_widget_show ( item );
4123 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4124 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4126 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4127 gtk_widget_show ( item );
4129 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4132 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4133 gtk_widget_show ( item );
4135 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4136 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4137 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4138 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4139 gtk_widget_show ( item );
4141 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4142 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4144 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4145 gtk_widget_show ( item );
4148 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4149 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4151 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4152 gtk_widget_show ( item );
4155 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4157 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4158 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4159 gtk_widget_show ( item );
4160 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4162 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4164 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4165 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4166 gtk_widget_show ( item );
4167 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4170 // Fake Waypoint UUIDs vi simple increasing integer
4171 static guint wp_uuid = 0;
4173 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4177 vik_waypoint_set_name (wp, name);
4179 if ( VIK_LAYER(vtl)->realized )
4181 // Do we need to create the sublayer:
4182 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4183 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4186 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4188 // Visibility column always needed for waypoints
4189 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 );
4191 // Actual setting of visibility dependent on the waypoint
4192 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4194 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4196 // Sort now as post_read is not called on a realized waypoint
4197 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4200 highest_wp_number_add_wp(vtl, name);
4201 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4205 // Fake Track UUIDs vi simple increasing integer
4206 static guint tr_uuid = 0;
4208 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4212 vik_track_set_name (t, name);
4214 if ( VIK_LAYER(vtl)->realized )
4216 // Do we need to create the sublayer:
4217 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4218 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4221 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4222 // Visibility column always needed for tracks
4223 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 );
4225 // Actual setting of visibility dependent on the track
4226 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4228 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4230 // Sort now as post_read is not called on a realized track
4231 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4234 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4236 trw_layer_update_treeview ( vtl, t );
4239 // Fake Route UUIDs vi simple increasing integer
4240 static guint rt_uuid = 0;
4242 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4246 vik_track_set_name (t, name);
4248 if ( VIK_LAYER(vtl)->realized )
4250 // Do we need to create the sublayer:
4251 if ( g_hash_table_size (vtl->routes) == 0 ) {
4252 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4255 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4256 // Visibility column always needed for routes
4257 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 );
4258 // Actual setting of visibility dependent on the route
4259 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4261 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4263 // Sort now as post_read is not called on a realized route
4264 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4267 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4269 trw_layer_update_treeview ( vtl, t );
4272 /* to be called whenever a track has been deleted or may have been changed. */
4273 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4275 if (vtl->current_tp_track == trk )
4276 trw_layer_cancel_current_tp ( vtl, FALSE );
4280 * Normally this is done to due the waypoint size preference having changed
4282 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4284 GHashTableIter iter;
4285 gpointer key, value;
4288 g_hash_table_iter_init ( &iter, vtl->waypoints );
4289 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4290 VikWaypoint *wp = VIK_WAYPOINT(value);
4292 // Reapply symbol setting to update the pixbuf
4293 gchar *tmp_symbol = g_strdup ( wp->symbol );
4294 vik_waypoint_set_symbol ( wp, tmp_symbol );
4295 g_free ( tmp_symbol );
4301 * trw_layer_new_unique_sublayer_name:
4303 * Allocates a unique new name
4305 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4308 gchar *newname = g_strdup(name);
4313 switch ( sublayer_type ) {
4314 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4315 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4317 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4318 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4321 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4324 // If found a name already in use try adding 1 to it and we try again
4326 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4328 newname = new_newname;
4331 } while ( id != NULL);
4336 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4338 // No more uniqueness of name forced when loading from a file
4339 // This now makes this function a little redunant as we just flow the parameters through
4340 vik_trw_layer_add_waypoint ( vtl, name, wp );
4343 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4345 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4346 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4347 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4348 vik_track_free ( tr );
4349 vtl->route_finder_append = FALSE; /* this means we have added it */
4352 // No more uniqueness of name forced when loading from a file
4354 vik_trw_layer_add_route ( vtl, name, tr );
4356 vik_trw_layer_add_track ( vtl, name, tr );
4358 if ( vtl->route_finder_check_added_track ) {
4359 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4360 vtl->route_finder_added_track = tr;
4365 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4367 *l = g_list_append(*l, id);
4371 * Move an item from one TRW layer to another TRW layer
4373 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4375 // TODO reconsider strategy when moving within layer (if anything...)
4376 gboolean rename = ( vtl_src != vtl_dest );
4380 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4381 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4385 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4387 newname = g_strdup ( trk->name );
4389 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4390 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4392 vik_trw_layer_delete_track ( vtl_src, trk );
4395 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4396 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4400 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4402 newname = g_strdup ( trk->name );
4404 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4405 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4407 vik_trw_layer_delete_route ( vtl_src, trk );
4410 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4411 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4415 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4417 newname = g_strdup ( wp->name );
4419 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4420 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4422 trw_layer_delete_waypoint ( vtl_src, wp );
4424 // Recalculate bounds even if not renamed as maybe dragged between layers
4425 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4426 trw_layer_calculate_bounds_waypoints ( vtl_src );
4430 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4432 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4433 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4435 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4436 GList *items = NULL;
4439 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4440 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4442 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4443 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4445 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4446 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4451 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4452 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4453 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4454 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4456 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4463 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4464 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4468 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4470 trku_udata *user_data = udata;
4471 if ( trk == user_data->trk ) {
4472 user_data->uuid = id;
4478 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4480 gboolean was_visible = FALSE;
4482 if ( trk && trk->name ) {
4484 if ( trk == vtl->current_track ) {
4485 vtl->current_track = NULL;
4486 vtl->current_tp_track = NULL;
4487 vtl->current_tp_id = NULL;
4488 vtl->moving_tp = FALSE;
4491 was_visible = trk->visible;
4493 if ( trk == vtl->route_finder_current_track )
4494 vtl->route_finder_current_track = NULL;
4496 if ( trk == vtl->route_finder_added_track )
4497 vtl->route_finder_added_track = NULL;
4503 // Hmmm, want key of it
4504 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4506 if ( trkf && udata.uuid ) {
4507 /* could be current_tp, so we have to check */
4508 trw_layer_cancel_tps_of_track ( vtl, trk );
4510 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4513 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4514 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4515 g_hash_table_remove ( vtl->tracks, udata.uuid );
4517 // If last sublayer, then remove sublayer container
4518 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4519 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4527 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4529 gboolean was_visible = FALSE;
4531 if ( trk && trk->name ) {
4533 if ( trk == vtl->current_track ) {
4534 vtl->current_track = NULL;
4535 vtl->current_tp_track = NULL;
4536 vtl->current_tp_id = NULL;
4537 vtl->moving_tp = FALSE;
4540 was_visible = trk->visible;
4542 if ( trk == vtl->route_finder_current_track )
4543 vtl->route_finder_current_track = NULL;
4545 if ( trk == vtl->route_finder_added_track )
4546 vtl->route_finder_added_track = NULL;
4552 // Hmmm, want key of it
4553 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4555 if ( trkf && udata.uuid ) {
4556 /* could be current_tp, so we have to check */
4557 trw_layer_cancel_tps_of_track ( vtl, trk );
4559 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4562 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4563 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4564 g_hash_table_remove ( vtl->routes, udata.uuid );
4566 // If last sublayer, then remove sublayer container
4567 if ( g_hash_table_size (vtl->routes) == 0 ) {
4568 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4576 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4578 gboolean was_visible = FALSE;
4580 if ( wp && wp->name ) {
4582 if ( wp == vtl->current_wp ) {
4583 vtl->current_wp = NULL;
4584 vtl->current_wp_id = NULL;
4585 vtl->moving_wp = FALSE;
4588 was_visible = wp->visible;
4594 // Hmmm, want key of it
4595 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4597 if ( wpf && udata.uuid ) {
4598 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4601 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4602 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4604 highest_wp_number_remove_wp(vtl, wp->name);
4605 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4607 // If last sublayer, then remove sublayer container
4608 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4609 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4619 // Only for temporary use by trw_layer_delete_waypoint_by_name
4620 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4622 wpu_udata *user_data = udata;
4623 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4624 user_data->uuid = id;
4631 * Delete a waypoint by the given name
4632 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4633 * as there be multiple waypoints with the same name
4635 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4638 // Fake a waypoint with the given name
4639 udata.wp = vik_waypoint_new ();
4640 vik_waypoint_set_name (udata.wp, name);
4641 // Currently only the name is used in this waypoint find function
4644 // Hmmm, want key of it
4645 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4647 vik_waypoint_free (udata.wp);
4649 if ( wpf && udata.uuid )
4650 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4656 VikTrack *trk; // input
4657 gpointer uuid; // output
4660 // Only for temporary use by trw_layer_delete_track_by_name
4661 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4663 tpu_udata *user_data = udata;
4664 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4665 user_data->uuid = id;
4672 * Delete a track by the given name
4673 * NOTE: ATM this will delete the first encountered Track with the specified name
4674 * as there may be multiple tracks with the same name within the specified hash table
4676 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4679 // Fake a track with the given name
4680 udata.trk = vik_track_new ();
4681 vik_track_set_name (udata.trk, name);
4682 // Currently only the name is used in this waypoint find function
4685 // Hmmm, want key of it
4686 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4688 vik_track_free (udata.trk);
4690 if ( trkf && udata.uuid ) {
4691 // This could be a little better written...
4692 if ( vtl->tracks == ht_tracks )
4693 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4694 if ( vtl->routes == ht_tracks )
4695 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4702 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4704 vik_treeview_item_delete (vt, it );
4707 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4710 vtl->current_track = NULL;
4711 vtl->route_finder_current_track = NULL;
4712 vtl->route_finder_added_track = NULL;
4713 if (vtl->current_tp_track)
4714 trw_layer_cancel_current_tp(vtl, FALSE);
4716 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4717 g_hash_table_remove_all(vtl->routes_iters);
4718 g_hash_table_remove_all(vtl->routes);
4720 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4722 vik_layer_emit_update ( VIK_LAYER(vtl) );
4725 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4728 vtl->current_track = NULL;
4729 vtl->route_finder_current_track = NULL;
4730 vtl->route_finder_added_track = NULL;
4731 if (vtl->current_tp_track)
4732 trw_layer_cancel_current_tp(vtl, FALSE);
4734 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4735 g_hash_table_remove_all(vtl->tracks_iters);
4736 g_hash_table_remove_all(vtl->tracks);
4738 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4740 vik_layer_emit_update ( VIK_LAYER(vtl) );
4743 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4745 vtl->current_wp = NULL;
4746 vtl->current_wp_id = NULL;
4747 vtl->moving_wp = FALSE;
4749 highest_wp_number_reset(vtl);
4751 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4752 g_hash_table_remove_all(vtl->waypoints_iters);
4753 g_hash_table_remove_all(vtl->waypoints);
4755 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4757 vik_layer_emit_update ( VIK_LAYER(vtl) );
4760 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4762 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4763 // Get confirmation from the user
4764 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4765 _("Are you sure you want to delete all tracks in %s?"),
4766 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4767 vik_trw_layer_delete_all_tracks (vtl);
4770 static void trw_layer_delete_all_routes ( menu_array_layer values )
4772 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4773 // Get confirmation from the user
4774 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4775 _("Are you sure you want to delete all routes in %s?"),
4776 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4777 vik_trw_layer_delete_all_routes (vtl);
4780 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4782 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4783 // Get confirmation from the user
4784 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4785 _("Are you sure you want to delete all waypoints in %s?"),
4786 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4787 vik_trw_layer_delete_all_waypoints (vtl);
4790 static void trw_layer_delete_item ( menu_array_sublayer values )
4792 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4793 gboolean was_visible = FALSE;
4794 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4796 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4797 if ( wp && wp->name ) {
4798 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4799 // Get confirmation from the user
4800 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4801 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4802 _("Are you sure you want to delete the waypoint \"%s\"?"),
4805 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4808 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4810 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4811 if ( trk && trk->name ) {
4812 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4813 // Get confirmation from the user
4814 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4815 _("Are you sure you want to delete the track \"%s\"?"),
4818 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4823 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4824 if ( trk && trk->name ) {
4825 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4826 // Get confirmation from the user
4827 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4828 _("Are you sure you want to delete the route \"%s\"?"),
4831 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4835 vik_layer_emit_update ( VIK_LAYER(vtl) );
4839 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4841 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4843 vik_waypoint_set_name ( wp, new_name );
4845 // Now update the treeview as well
4850 // Need key of it for treeview update
4851 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4853 if ( wpf && udataU.uuid ) {
4854 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4857 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4858 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4864 * Maintain icon of waypoint in the treeview
4866 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4868 // update the treeview
4873 // Need key of it for treeview update
4874 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4876 if ( wpf && udataU.uuid ) {
4877 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4880 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4885 static void trw_layer_properties_item ( menu_array_sublayer values )
4887 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4888 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4890 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4892 if ( wp && wp->name )
4894 gboolean updated = FALSE;
4895 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4897 trw_layer_waypoint_rename ( vtl, wp, new_name );
4899 if ( updated && values[MA_TV_ITER] )
4900 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4902 if ( updated && VIK_LAYER(vtl)->visible )
4903 vik_layer_emit_update ( VIK_LAYER(vtl) );
4909 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4910 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4912 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4914 if ( tr && tr->name )
4916 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4927 * trw_layer_track_statistics:
4929 * Show track statistics.
4930 * ATM jump to the stats page in the properties
4931 * TODO: consider separating the stats into an individual dialog?
4933 static void trw_layer_track_statistics ( menu_array_sublayer values )
4935 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4937 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4938 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4940 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4942 if ( trk && trk->name ) {
4943 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4953 * Update the treeview of the track id - primarily to update the icon
4955 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4961 gpointer *trkf = NULL;
4962 if ( trk->is_route )
4963 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4965 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4967 if ( trkf && udata.uuid ) {
4969 GtkTreeIter *iter = NULL;
4970 if ( trk->is_route )
4971 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4973 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4976 // TODO: Make this a function
4977 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4978 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4979 ((trk->color.green & 0xff00) << 8) |
4980 (trk->color.blue & 0xff00);
4981 gdk_pixbuf_fill ( pixbuf, pixel );
4982 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4983 g_object_unref (pixbuf);
4990 Parameter 1 -> VikLayersPanel
4991 Parameter 2 -> VikLayer
4992 Parameter 3 -> VikViewport
4994 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4997 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4998 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5001 /* since vlp not set, vl & vvp should be valid instead! */
5003 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
5004 vik_layer_emit_update ( VIK_LAYER(vl) );
5009 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5011 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5013 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5014 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5016 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5018 if ( track && track->trackpoints )
5019 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5022 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5024 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5026 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5027 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5029 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5031 if ( track && track->trackpoints )
5033 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5035 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5036 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5037 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5038 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5039 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5043 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5045 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5047 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5048 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5050 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5055 // Converting a track to a route can be a bit more complicated,
5056 // so give a chance to change our minds:
5057 if ( !trk->is_route &&
5058 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5059 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5061 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5062 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5067 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5070 trk_copy->is_route = !trk_copy->is_route;
5072 // ATM can't set name to self - so must create temporary copy
5073 gchar *name = g_strdup ( trk_copy->name );
5075 // Delete old one and then add new one
5076 if ( trk->is_route ) {
5077 vik_trw_layer_delete_route ( vtl, trk );
5078 vik_trw_layer_add_track ( vtl, name, trk_copy );
5081 // Extra route conversion bits...
5082 vik_track_merge_segments ( trk_copy );
5083 vik_track_to_routepoints ( trk_copy );
5085 vik_trw_layer_delete_track ( vtl, trk );
5086 vik_trw_layer_add_route ( vtl, name, trk_copy );
5090 // Update in case color of track / route changes when moving between sublayers
5091 vik_layer_emit_update ( VIK_LAYER(vtl) );
5094 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5096 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5098 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5099 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5101 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5104 vik_track_anonymize_times ( track );
5107 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5109 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5111 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5112 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5114 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5119 vtl->current_track = track;
5120 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);
5122 if ( track->trackpoints )
5123 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5127 * extend a track using route finder
5129 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5131 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5132 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5135 if ( !track->trackpoints )
5138 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5139 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5140 vtl->route_finder_current_track = track;
5141 vtl->route_finder_started = TRUE;
5143 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5149 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5151 // If have a vlp then perform a basic test to see if any DEM info available...
5153 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5155 if ( !g_list_length(dems) ) {
5156 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5164 * apply_dem_data_common:
5166 * A common function for applying the DEM values and reporting the results.
5168 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5170 if ( !trw_layer_dem_test ( vtl, vlp ) )
5173 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5174 // Inform user how much was changed
5176 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5177 g_snprintf(str, 64, tmp_str, changed);
5178 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5181 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5183 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5185 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5186 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5188 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5191 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5194 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5196 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5198 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5199 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5201 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5204 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5210 * A common function for applying the elevation smoothing and reporting the results.
5212 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5214 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5215 // Inform user how much was changed
5217 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5218 g_snprintf(str, 64, tmp_str, changed);
5219 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5225 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5227 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5229 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5230 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5232 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5237 smooth_it ( vtl, track, FALSE );
5240 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5242 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5244 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5245 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5247 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5252 smooth_it ( vtl, track, TRUE );
5256 * Commonal helper function
5258 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5261 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5262 g_snprintf(str, 64, tmp_str, changed);
5263 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5266 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5268 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5269 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5271 if ( !trw_layer_dem_test ( vtl, vlp ) )
5275 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5277 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5279 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5283 GHashTableIter iter;
5284 gpointer key, value;
5286 g_hash_table_iter_init ( &iter, vtl->waypoints );
5287 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5288 VikWaypoint *wp = VIK_WAYPOINT(value);
5289 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5292 wp_changed_message ( vtl, changed );
5295 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5297 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5298 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5300 if ( !trw_layer_dem_test ( vtl, vlp ) )
5304 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5306 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5308 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5312 GHashTableIter iter;
5313 gpointer key, value;
5315 g_hash_table_iter_init ( &iter, vtl->waypoints );
5316 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5317 VikWaypoint *wp = VIK_WAYPOINT(value);
5318 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5321 wp_changed_message ( vtl, changed );
5324 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5326 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5328 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5329 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5331 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5335 if ( !track->trackpoints )
5337 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5340 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5342 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5344 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5345 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5347 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5352 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5355 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5358 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5360 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5362 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5363 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5365 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5370 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5373 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5376 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5378 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5380 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5381 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5383 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5388 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5391 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5395 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5397 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5399 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5401 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5402 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5404 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5406 if ( trk && trk->trackpoints )
5408 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5409 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5410 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5411 if ( values[MA_VLP] )
5412 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5414 vik_layer_emit_update ( VIK_LAYER(vtl) );
5419 * Refine the selected track/route with a routing engine.
5420 * The routing engine is selected by the user, when requestiong the job.
5422 static void trw_layer_route_refine ( menu_array_sublayer values )
5424 static gint last_engine = 0;
5425 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5428 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5429 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5431 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5433 if ( trk && trk->trackpoints )
5435 /* Check size of the route */
5436 int nb = vik_track_get_tp_count(trk);
5438 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5439 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5440 GTK_MESSAGE_WARNING,
5441 GTK_BUTTONS_OK_CANCEL,
5442 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5444 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5445 gtk_widget_destroy ( dialog );
5446 if (response != GTK_RESPONSE_OK )
5449 /* Select engine from dialog */
5450 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5451 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5452 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5454 GTK_RESPONSE_REJECT,
5456 GTK_RESPONSE_ACCEPT,
5458 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5459 gtk_widget_show_all(label);
5461 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5463 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5464 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5465 gtk_widget_show_all(combo);
5467 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5469 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5471 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5473 /* Dialog validated: retrieve selected engine and do the job */
5474 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5475 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5478 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5480 /* Force saving track */
5481 /* FIXME: remove or rename this hack */
5482 vtl->route_finder_check_added_track = TRUE;
5485 vik_routing_engine_refine (routing, vtl, trk);
5487 /* FIXME: remove or rename this hack */
5488 if ( vtl->route_finder_added_track )
5489 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5491 vtl->route_finder_added_track = NULL;
5492 vtl->route_finder_check_added_track = FALSE;
5494 vik_layer_emit_update ( VIK_LAYER(vtl) );
5496 /* Restore cursor */
5497 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5499 gtk_widget_destroy ( dialog );
5503 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5505 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5506 trw_layer_tpwin_init ( vtl );
5509 /*************************************
5510 * merge/split by time routines
5511 *************************************/
5513 /* called for each key in track hash table.
5514 * If the current track has the same time stamp type, add it to the result,
5515 * except the one pointed by "exclude".
5516 * set exclude to NULL if there is no exclude to check.
5517 * Note that the result is in reverse (for performance reasons).
5522 gboolean with_timestamps;
5524 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5526 twt_udata *user_data = udata;
5527 VikTrackpoint *p1, *p2;
5528 VikTrack *trk = VIK_TRACK(value);
5529 if (trk == (VikTrack *)user_data->exclude) {
5533 if (trk->trackpoints) {
5534 p1 = vik_track_get_tp_first(trk);
5535 p2 = vik_track_get_tp_last(trk);
5537 if ( user_data->with_timestamps ) {
5538 if (!p1->has_timestamp || !p2->has_timestamp) {
5543 // Don't add tracks with timestamps when getting non timestamp tracks
5544 if (p1->has_timestamp || p2->has_timestamp) {
5550 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5553 /* called for each key in track hash table. if original track user_data[1] is close enough
5554 * to the passed one, add it to list in user_data[0]
5556 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5559 VikTrackpoint *p1, *p2;
5560 VikTrack *trk = VIK_TRACK(value);
5562 GList **nearby_tracks = ((gpointer *)user_data)[0];
5563 GList *tpoints = ((gpointer *)user_data)[1];
5566 * detect reasons for not merging, and return
5567 * if no reason is found not to merge, then do it.
5570 // Exclude the original track from the compiled list
5571 if (trk->trackpoints == tpoints) {
5575 t1 = vik_track_get_tp_first(trk)->timestamp;
5576 t2 = vik_track_get_tp_last(trk)->timestamp;
5578 if (trk->trackpoints) {
5579 p1 = vik_track_get_tp_first(trk);
5580 p2 = vik_track_get_tp_last(trk);
5582 if (!p1->has_timestamp || !p2->has_timestamp) {
5583 //g_print("no timestamp\n");
5587 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5588 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5589 if (! (abs(t1 - p2->timestamp) < threshold ||
5591 abs(p1->timestamp - t2) < threshold)
5598 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5601 /* comparison function used to sort tracks; a and b are hash table keys */
5602 /* Not actively used - can be restored if needed
5603 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5605 GHashTable *tracks = user_data;
5608 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5609 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5611 if (t1 < t2) return -1;
5612 if (t1 > t2) return 1;
5617 /* comparison function used to sort trackpoints */
5618 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5620 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5622 if (t1 < t2) return -1;
5623 if (t1 > t2) return 1;
5628 * comparison function which can be used to sort tracks or waypoints by name
5630 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5632 const gchar* namea = (const gchar*) a;
5633 const gchar* nameb = (const gchar*) b;
5634 if ( namea == NULL || nameb == NULL)
5637 // Same sort method as used in the vik_treeview_*_alphabetize functions
5638 return strcmp ( namea, nameb );
5642 * Attempt to merge selected track with other tracks specified by the user
5643 * Tracks to merge with must be of the same 'type' as the selected track -
5644 * either all with timestamps, or all without timestamps
5646 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5648 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5649 GList *other_tracks = NULL;
5650 GHashTable *ght_tracks;
5651 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5652 ght_tracks = vtl->routes;
5654 ght_tracks = vtl->tracks;
5656 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5661 if ( !track->trackpoints )
5665 udata.result = &other_tracks;
5666 udata.exclude = track->trackpoints;
5667 // Allow merging with 'similar' time type time tracks
5668 // i.e. either those times, or those without
5669 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5671 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5672 other_tracks = g_list_reverse(other_tracks);
5674 if ( !other_tracks ) {
5675 if ( udata.with_timestamps )
5676 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5678 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5682 // Sort alphabetically for user presentation
5683 // Convert into list of names for usage with dialog function
5684 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5685 GList *other_tracks_names = NULL;
5686 GList *iter = g_list_first ( other_tracks );
5688 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5689 iter = g_list_next ( iter );
5692 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5694 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5698 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5699 g_list_free(other_tracks);
5700 g_list_free(other_tracks_names);
5705 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5706 VikTrack *merge_track;
5707 if ( track->is_route )
5708 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5710 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5713 vik_track_steal_and_append_trackpoints ( track, merge_track );
5714 if ( track->is_route )
5715 vik_trw_layer_delete_route (vtl, merge_track);
5717 vik_trw_layer_delete_track (vtl, merge_track);
5718 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5721 for (l = merge_list; l != NULL; l = g_list_next(l))
5723 g_list_free(merge_list);
5725 vik_layer_emit_update( VIK_LAYER(vtl) );
5729 // c.f. trw_layer_sorted_track_id_by_name_list
5730 // but don't add the specified track to the list (normally current track)
5731 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5733 twt_udata *user_data = udata;
5736 if (trk->trackpoints == user_data->exclude) {
5740 // Sort named list alphabetically
5741 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5745 * Join - this allows combining 'tracks' and 'track routes'
5746 * i.e. doesn't care about whether tracks have consistent timestamps
5747 * ATM can only append one track at a time to the currently selected track
5749 static void trw_layer_append_track ( menu_array_sublayer values )
5752 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5754 GHashTable *ght_tracks;
5755 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5756 ght_tracks = vtl->routes;
5758 ght_tracks = vtl->tracks;
5760 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5765 GList *other_tracks_names = NULL;
5767 // Sort alphabetically for user presentation
5768 // Convert into list of names for usage with dialog function
5769 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5771 udata.result = &other_tracks_names;
5772 udata.exclude = trk->trackpoints;
5774 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5776 // Note the limit to selecting one track only
5777 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5778 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5779 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5782 trk->is_route ? _("Append Route"): _("Append Track"),
5783 trk->is_route ? _("Select the route to append after the current route") :
5784 _("Select the track to append after the current track") );
5786 g_list_free(other_tracks_names);
5788 // It's a list, but shouldn't contain more than one other track!
5789 if ( append_list ) {
5791 for (l = append_list; l != NULL; l = g_list_next(l)) {
5792 // TODO: at present this uses the first track found by name,
5793 // which with potential multiple same named tracks may not be the one selected...
5794 VikTrack *append_track;
5795 if ( trk->is_route )
5796 append_track = vik_trw_layer_get_route ( vtl, l->data );
5798 append_track = vik_trw_layer_get_track ( vtl, l->data );
5800 if ( append_track ) {
5801 vik_track_steal_and_append_trackpoints ( trk, append_track );
5802 if ( trk->is_route )
5803 vik_trw_layer_delete_route (vtl, append_track);
5805 vik_trw_layer_delete_track (vtl, append_track);
5808 for (l = append_list; l != NULL; l = g_list_next(l))
5810 g_list_free(append_list);
5812 vik_layer_emit_update( VIK_LAYER(vtl) );
5817 * Very similar to trw_layer_append_track for joining
5818 * but this allows selection from the 'other' list
5819 * If a track is selected, then is shows routes and joins the selected one
5820 * If a route is selected, then is shows tracks and joins the selected one
5822 static void trw_layer_append_other ( menu_array_sublayer values )
5825 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5827 GHashTable *ght_mykind, *ght_others;
5828 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5829 ght_mykind = vtl->routes;
5830 ght_others = vtl->tracks;
5833 ght_mykind = vtl->tracks;
5834 ght_others = vtl->routes;
5837 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5842 GList *other_tracks_names = NULL;
5844 // Sort alphabetically for user presentation
5845 // Convert into list of names for usage with dialog function
5846 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5848 udata.result = &other_tracks_names;
5849 udata.exclude = trk->trackpoints;
5851 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5853 // Note the limit to selecting one track only
5854 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5855 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5856 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5859 trk->is_route ? _("Append Track"): _("Append Route"),
5860 trk->is_route ? _("Select the track to append after the current route") :
5861 _("Select the route to append after the current track") );
5863 g_list_free(other_tracks_names);
5865 // It's a list, but shouldn't contain more than one other track!
5866 if ( append_list ) {
5868 for (l = append_list; l != NULL; l = g_list_next(l)) {
5869 // TODO: at present this uses the first track found by name,
5870 // which with potential multiple same named tracks may not be the one selected...
5872 // Get FROM THE OTHER TYPE list
5873 VikTrack *append_track;
5874 if ( trk->is_route )
5875 append_track = vik_trw_layer_get_track ( vtl, l->data );
5877 append_track = vik_trw_layer_get_route ( vtl, l->data );
5879 if ( append_track ) {
5881 if ( !append_track->is_route &&
5882 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5883 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5885 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5886 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5887 vik_track_merge_segments ( append_track );
5888 vik_track_to_routepoints ( append_track );
5895 vik_track_steal_and_append_trackpoints ( trk, append_track );
5897 // Delete copied which is FROM THE OTHER TYPE list
5898 if ( trk->is_route )
5899 vik_trw_layer_delete_track (vtl, append_track);
5901 vik_trw_layer_delete_route (vtl, append_track);
5904 for (l = append_list; l != NULL; l = g_list_next(l))
5906 g_list_free(append_list);
5907 vik_layer_emit_update( VIK_LAYER(vtl) );
5911 /* merge by segments */
5912 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5914 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5915 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5916 guint segments = vik_track_merge_segments ( trk );
5917 // NB currently no need to redraw as segments not actually shown on the display
5918 // However inform the user of what happened:
5920 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5921 g_snprintf(str, 64, tmp_str, segments);
5922 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5925 /* merge by time routine */
5926 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5928 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5932 GList *tracks_with_timestamp = NULL;
5933 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5934 if (orig_trk->trackpoints &&
5935 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
5936 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5941 udata.result = &tracks_with_timestamp;
5942 udata.exclude = orig_trk->trackpoints;
5943 udata.with_timestamps = TRUE;
5944 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5945 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5947 if (!tracks_with_timestamp) {
5948 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5951 g_list_free(tracks_with_timestamp);
5953 static guint threshold_in_minutes = 1;
5954 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5955 _("Merge Threshold..."),
5956 _("Merge when time between tracks less than:"),
5957 &threshold_in_minutes)) {
5961 // keep attempting to merge all tracks until no merges within the time specified is possible
5962 gboolean attempt_merge = TRUE;
5963 GList *nearby_tracks = NULL;
5965 static gpointer params[3];
5967 while ( attempt_merge ) {
5969 // Don't try again unless tracks have changed
5970 attempt_merge = FALSE;
5972 trps = orig_trk->trackpoints;
5976 if (nearby_tracks) {
5977 g_list_free(nearby_tracks);
5978 nearby_tracks = NULL;
5981 params[0] = &nearby_tracks;
5982 params[1] = (gpointer)trps;
5983 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5985 /* get a list of adjacent-in-time tracks */
5986 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5989 GList *l = nearby_tracks;
5991 /* remove trackpoints from merged track, delete track */
5992 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5993 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5995 // Tracks have changed, therefore retry again against all the remaining tracks
5996 attempt_merge = TRUE;
6001 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6004 g_list_free(nearby_tracks);
6006 vik_layer_emit_update( VIK_LAYER(vtl) );
6010 * Split a track at the currently selected trackpoint
6012 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6014 if ( !vtl->current_tpl )
6017 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6018 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6020 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6021 GList *newglist = g_list_alloc ();
6022 newglist->prev = NULL;
6023 newglist->next = vtl->current_tpl->next;
6024 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6025 tr->trackpoints = newglist;
6027 vtl->current_tpl->next->prev = newglist; /* end old track here */
6028 vtl->current_tpl->next = NULL;
6030 // Bounds of the selected track changed due to the split
6031 vik_track_calculate_bounds ( vtl->current_tp_track );
6033 vtl->current_tpl = newglist; /* change tp to first of new track. */
6034 vtl->current_tp_track = tr;
6037 vik_trw_layer_add_route ( vtl, name, tr );
6039 vik_trw_layer_add_track ( vtl, name, tr );
6041 // Bounds of the new track created by the split
6042 vik_track_calculate_bounds ( tr );
6048 // Also need id of newly created track
6051 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6053 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6055 if ( trkf && udata.uuid )
6056 vtl->current_tp_id = udata.uuid;
6058 vtl->current_tp_id = NULL;
6060 vik_layer_emit_update(VIK_LAYER(vtl));
6066 /* split by time routine */
6067 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6069 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6070 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6071 GList *trps = track->trackpoints;
6073 GList *newlists = NULL;
6074 GList *newtps = NULL;
6075 static guint thr = 1;
6082 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6083 _("Split Threshold..."),
6084 _("Split when time between trackpoints exceeds:"),
6089 /* iterate through trackpoints, and copy them into new lists without touching original list */
6090 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6094 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6096 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6099 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6100 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6101 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6103 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6108 if (ts - prev_ts > thr*60) {
6109 /* flush accumulated trackpoints into new list */
6110 newlists = g_list_append(newlists, g_list_reverse(newtps));
6114 /* accumulate trackpoint copies in newtps, in reverse order */
6115 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6117 iter = g_list_next(iter);
6120 newlists = g_list_append(newlists, g_list_reverse(newtps));
6123 /* put lists of trackpoints into tracks */
6125 // Only bother updating if the split results in new tracks
6126 if (g_list_length (newlists) > 1) {
6131 tr = vik_track_copy ( track, FALSE );
6132 tr->trackpoints = (GList *)(iter->data);
6134 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6135 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6136 g_free ( new_tr_name );
6137 vik_track_calculate_bounds ( tr );
6138 iter = g_list_next(iter);
6140 // Remove original track and then update the display
6141 vik_trw_layer_delete_track (vtl, track);
6142 vik_layer_emit_update(VIK_LAYER(vtl));
6144 g_list_free(newlists);
6148 * Split a track by the number of points as specified by the user
6150 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6152 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6154 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6155 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6157 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6162 // Check valid track
6163 GList *trps = track->trackpoints;
6167 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6168 _("Split Every Nth Point"),
6169 _("Split on every Nth point:"),
6170 250, // Default value as per typical limited track capacity of various GPS devices
6174 // Was a valid number returned?
6180 GList *newlists = NULL;
6181 GList *newtps = NULL;
6186 /* accumulate trackpoint copies in newtps, in reverse order */
6187 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6189 if (count >= points) {
6190 /* flush accumulated trackpoints into new list */
6191 newlists = g_list_append(newlists, g_list_reverse(newtps));
6195 iter = g_list_next(iter);
6198 // If there is a remaining chunk put that into the new split list
6199 // This may well be the whole track if no split points were encountered
6201 newlists = g_list_append(newlists, g_list_reverse(newtps));
6204 /* put lists of trackpoints into tracks */
6206 // Only bother updating if the split results in new tracks
6207 if (g_list_length (newlists) > 1) {
6212 tr = vik_track_copy ( track, FALSE );
6213 tr->trackpoints = (GList *)(iter->data);
6215 if ( track->is_route ) {
6216 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6217 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6220 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6221 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6223 g_free ( new_tr_name );
6224 vik_track_calculate_bounds ( tr );
6226 iter = g_list_next(iter);
6228 // Remove original track and then update the display
6229 if ( track->is_route )
6230 vik_trw_layer_delete_route (vtl, track);
6232 vik_trw_layer_delete_track (vtl, track);
6233 vik_layer_emit_update(VIK_LAYER(vtl));
6235 g_list_free(newlists);
6239 * Split a track at the currently selected trackpoint
6241 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6243 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6244 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6245 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6249 * Split a track by its segments
6250 * Routes do not have segments so don't call this for routes
6252 static void trw_layer_split_segments ( menu_array_sublayer values )
6254 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6255 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6262 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6265 for ( i = 0; i < ntracks; i++ ) {
6267 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6268 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6269 g_free ( new_tr_name );
6274 // Remove original track
6275 vik_trw_layer_delete_track ( vtl, trk );
6276 vik_layer_emit_update ( VIK_LAYER(vtl) );
6279 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6282 /* end of split/merge routines */
6284 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6288 // Find available adjacent trackpoint
6289 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6290 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6291 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6293 // Delete current trackpoint
6294 vik_trackpoint_free ( vtl->current_tpl->data );
6295 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6297 // Set to current to the available adjacent trackpoint
6298 vtl->current_tpl = new_tpl;
6300 if ( vtl->current_tp_track ) {
6301 vik_track_calculate_bounds ( vtl->current_tp_track );
6305 // Delete current trackpoint
6306 vik_trackpoint_free ( vtl->current_tpl->data );
6307 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6308 trw_layer_cancel_current_tp ( vtl, FALSE );
6313 * Delete the selected point
6315 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6317 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6319 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6320 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6322 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6327 if ( !vtl->current_tpl )
6330 trw_layer_trackpoint_selected_delete ( vtl, trk );
6332 // Track has been updated so update tps:
6333 trw_layer_cancel_tps_of_track ( vtl, trk );
6335 vik_layer_emit_update ( VIK_LAYER(vtl) );
6339 * Delete adjacent track points at the same position
6340 * AKA Delete Dulplicates on the Properties Window
6342 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6344 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6346 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6347 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6349 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6354 gulong removed = vik_track_remove_dup_points ( trk );
6356 // Track has been updated so update tps:
6357 trw_layer_cancel_tps_of_track ( vtl, trk );
6359 // Inform user how much was deleted as it's not obvious from the normal view
6361 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6362 g_snprintf(str, 64, tmp_str, removed);
6363 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6365 vik_layer_emit_update ( VIK_LAYER(vtl) );
6369 * Delete adjacent track points with the same timestamp
6370 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6372 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6374 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6376 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6377 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6379 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6384 gulong removed = vik_track_remove_same_time_points ( trk );
6386 // Track has been updated so update tps:
6387 trw_layer_cancel_tps_of_track ( vtl, trk );
6389 // Inform user how much was deleted as it's not obvious from the normal view
6391 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6392 g_snprintf(str, 64, tmp_str, removed);
6393 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6395 vik_layer_emit_update ( VIK_LAYER(vtl) );
6401 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6403 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6405 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6406 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6408 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6413 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6415 vik_layer_emit_update ( VIK_LAYER(vtl) );
6418 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6420 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6422 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6423 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6425 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6430 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6432 vik_layer_emit_update ( VIK_LAYER(vtl) );
6438 static void trw_layer_reverse ( menu_array_sublayer values )
6440 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6442 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6443 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6445 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6450 vik_track_reverse ( track );
6452 vik_layer_emit_update ( VIK_LAYER(vtl) );
6456 * Similar to trw_layer_enum_item, but this uses a sorted method
6459 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6461 GList **list = (GList**)udata;
6462 // *list = g_list_prepend(*all, key); //unsorted method
6463 // Sort named list alphabetically
6464 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6469 * Now Waypoint specific sort
6471 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6473 GList **list = (GList**)udata;
6474 // Sort named list alphabetically
6475 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6479 * Track specific sort
6481 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6483 GList **list = (GList**)udata;
6484 // Sort named list alphabetically
6485 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6490 gboolean has_same_track_name;
6491 const gchar *same_track_name;
6492 } same_track_name_udata;
6494 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6496 const gchar* namea = (const gchar*) aa;
6497 const gchar* nameb = (const gchar*) bb;
6500 gint result = strcmp ( namea, nameb );
6502 if ( result == 0 ) {
6503 // Found two names the same
6504 same_track_name_udata *user_data = udata;
6505 user_data->has_same_track_name = TRUE;
6506 user_data->same_track_name = namea;
6509 // Leave ordering the same
6514 * Find out if any tracks have the same name in this hash table
6516 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6518 // Sort items by name, then compare if any next to each other are the same
6520 GList *track_names = NULL;
6521 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6524 if ( ! track_names )
6527 same_track_name_udata udata;
6528 udata.has_same_track_name = FALSE;
6530 // Use sort routine to traverse list comparing items
6531 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6532 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6533 // Still no tracks...
6537 return udata.has_same_track_name;
6541 * Force unqiue track names for the track table specified
6542 * Note the panel is a required parameter to enable the update of the names displayed
6543 * Specify if on tracks or else on routes
6545 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6547 // . Search list for an instance of repeated name
6548 // . get track of this name
6549 // . create new name
6550 // . rename track & update equiv. treeview iter
6551 // . repeat until all different
6553 same_track_name_udata udata;
6555 GList *track_names = NULL;
6556 udata.has_same_track_name = FALSE;
6557 udata.same_track_name = NULL;
6559 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6562 if ( ! track_names )
6565 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6567 // Still no tracks...
6568 if ( ! dummy_list1 )
6571 while ( udata.has_same_track_name ) {
6573 // Find a track with the same name
6576 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6578 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6582 g_critical("Houston, we've had a problem.");
6583 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6584 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6589 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6590 vik_track_set_name ( trk, newname );
6596 // Need want key of it for treeview update
6597 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6599 if ( trkf && udataU.uuid ) {
6603 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6605 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6608 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6610 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6612 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6616 // Start trying to find same names again...
6618 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6619 udata.has_same_track_name = FALSE;
6620 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6622 // No tracks any more - give up searching
6623 if ( ! dummy_list2 )
6624 udata.has_same_track_name = FALSE;
6628 vik_layers_panel_emit_update ( vlp );
6631 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6633 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6636 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6637 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6638 iter = &(vtl->tracks_iter);
6639 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6641 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6642 iter = &(vtl->routes_iter);
6643 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6645 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6646 iter = &(vtl->waypoints_iter);
6647 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6651 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6654 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6656 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6659 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6660 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6661 iter = &(vtl->tracks_iter);
6662 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6664 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6665 iter = &(vtl->routes_iter);
6666 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6668 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6669 iter = &(vtl->waypoints_iter);
6670 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6674 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6680 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6682 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6685 // Ensure list of track names offered is unique
6686 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6687 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6688 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6689 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6695 // Sort list alphabetically for better presentation
6696 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6699 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6703 // Get list of items to delete from the user
6704 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6707 _("Delete Selection"),
6708 _("Select tracks to delete"));
6711 // Delete requested tracks
6712 // since specificly requested, IMHO no need for extra confirmation
6713 if ( delete_list ) {
6715 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6716 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6717 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6719 g_list_free(delete_list);
6720 vik_layer_emit_update( VIK_LAYER(vtl) );
6727 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6729 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6732 // Ensure list of track names offered is unique
6733 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6734 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6735 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6736 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6742 // Sort list alphabetically for better presentation
6743 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6746 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6750 // Get list of items to delete from the user
6751 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6754 _("Delete Selection"),
6755 _("Select routes to delete") );
6758 // Delete requested routes
6759 // since specificly requested, IMHO no need for extra confirmation
6760 if ( delete_list ) {
6762 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6763 // This deletes first route it finds of that name (but uniqueness is enforced above)
6764 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6766 g_list_free(delete_list);
6767 vik_layer_emit_update( VIK_LAYER(vtl) );
6772 gboolean has_same_waypoint_name;
6773 const gchar *same_waypoint_name;
6774 } same_waypoint_name_udata;
6776 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6778 const gchar* namea = (const gchar*) aa;
6779 const gchar* nameb = (const gchar*) bb;
6782 gint result = strcmp ( namea, nameb );
6784 if ( result == 0 ) {
6785 // Found two names the same
6786 same_waypoint_name_udata *user_data = udata;
6787 user_data->has_same_waypoint_name = TRUE;
6788 user_data->same_waypoint_name = namea;
6791 // Leave ordering the same
6796 * Find out if any waypoints have the same name in this layer
6798 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6800 // Sort items by name, then compare if any next to each other are the same
6802 GList *waypoint_names = NULL;
6803 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6806 if ( ! waypoint_names )
6809 same_waypoint_name_udata udata;
6810 udata.has_same_waypoint_name = FALSE;
6812 // Use sort routine to traverse list comparing items
6813 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6814 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6815 // Still no waypoints...
6819 return udata.has_same_waypoint_name;
6823 * Force unqiue waypoint names for this layer
6824 * Note the panel is a required parameter to enable the update of the names displayed
6826 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6828 // . Search list for an instance of repeated name
6829 // . get waypoint of this name
6830 // . create new name
6831 // . rename waypoint & update equiv. treeview iter
6832 // . repeat until all different
6834 same_waypoint_name_udata udata;
6836 GList *waypoint_names = NULL;
6837 udata.has_same_waypoint_name = FALSE;
6838 udata.same_waypoint_name = NULL;
6840 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6843 if ( ! waypoint_names )
6846 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6848 // Still no waypoints...
6849 if ( ! dummy_list1 )
6852 while ( udata.has_same_waypoint_name ) {
6854 // Find a waypoint with the same name
6855 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6859 g_critical("Houston, we've had a problem.");
6860 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6861 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6866 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6868 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6870 // Start trying to find same names again...
6871 waypoint_names = NULL;
6872 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6873 udata.has_same_waypoint_name = FALSE;
6874 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6876 // No waypoints any more - give up searching
6877 if ( ! dummy_list2 )
6878 udata.has_same_waypoint_name = FALSE;
6882 vik_layers_panel_emit_update ( vlp );
6888 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6890 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6893 // Ensure list of waypoint names offered is unique
6894 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6895 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6896 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6897 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6903 // Sort list alphabetically for better presentation
6904 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6906 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6910 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
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 waypoints to delete"));
6920 // Delete requested waypoints
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 waypoint it finds of that name (but uniqueness is enforced above)
6926 trw_layer_delete_waypoint_by_name (vtl, l->data);
6928 g_list_free(delete_list);
6930 trw_layer_calculate_bounds_waypoints ( vtl );
6931 vik_layer_emit_update( VIK_LAYER(vtl) );
6939 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6941 vik_treeview_item_toggle_visible ( vt, it );
6947 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6949 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6955 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6957 wp->visible = GPOINTER_TO_INT (on_off);
6963 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6965 wp->visible = !wp->visible;
6971 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
6973 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6974 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6975 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6976 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6978 vik_layer_emit_update ( VIK_LAYER(vtl) );
6984 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
6986 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6987 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6988 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6989 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6991 vik_layer_emit_update ( VIK_LAYER(vtl) );
6997 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
6999 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7000 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7001 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7003 vik_layer_emit_update ( VIK_LAYER(vtl) );
7009 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7011 trk->visible = GPOINTER_TO_INT (on_off);
7017 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7019 trk->visible = !trk->visible;
7025 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7027 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7028 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7029 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7030 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7032 vik_layer_emit_update ( VIK_LAYER(vtl) );
7038 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7040 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7041 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7042 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7043 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7045 vik_layer_emit_update ( VIK_LAYER(vtl) );
7051 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7053 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7054 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7055 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7057 vik_layer_emit_update ( VIK_LAYER(vtl) );
7063 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7065 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7066 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7067 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7068 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7070 vik_layer_emit_update ( VIK_LAYER(vtl) );
7076 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7078 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7079 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7080 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7081 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7083 vik_layer_emit_update ( VIK_LAYER(vtl) );
7089 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7091 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7092 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7093 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7095 vik_layer_emit_update ( VIK_LAYER(vtl) );
7099 * vik_trw_layer_build_waypoint_list_t:
7101 * Helper function to construct a list of #vik_trw_waypoint_list_t
7103 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7105 GList *waypoints_and_layers = NULL;
7106 // build waypoints_and_layers list
7107 while ( waypoints ) {
7108 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7109 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7111 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7112 waypoints = g_list_next ( waypoints );
7114 return waypoints_and_layers;
7118 * trw_layer_create_waypoint_list:
7120 * Create the latest list of waypoints with the associated layer(s)
7121 * Although this will always be from a single layer here
7123 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7125 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7126 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7128 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7132 * trw_layer_analyse_close:
7134 * Stuff to do on dialog closure
7136 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7138 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7139 gtk_widget_destroy ( dialog );
7140 vtl->tracks_analysis_dialog = NULL;
7144 * vik_trw_layer_build_track_list_t:
7146 * Helper function to construct a list of #vik_trw_track_list_t
7148 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7150 GList *tracks_and_layers = NULL;
7151 // build tracks_and_layers list
7153 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7154 vtdl->trk = VIK_TRACK(tracks->data);
7156 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7157 tracks = g_list_next ( tracks );
7159 return tracks_and_layers;
7163 * trw_layer_create_track_list:
7165 * Create the latest list of tracks with the associated layer(s)
7166 * Although this will always be from a single layer here
7168 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7170 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7171 GList *tracks = NULL;
7172 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7173 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7175 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7177 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7180 static void trw_layer_tracks_stats ( menu_array_layer values )
7182 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7183 // There can only be one!
7184 if ( vtl->tracks_analysis_dialog )
7187 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7188 VIK_LAYER(vtl)->name,
7190 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7191 trw_layer_create_track_list,
7192 trw_layer_analyse_close );
7198 static void trw_layer_routes_stats ( menu_array_layer values )
7200 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7201 // There can only be one!
7202 if ( vtl->tracks_analysis_dialog )
7205 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7206 VIK_LAYER(vtl)->name,
7208 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7209 trw_layer_create_track_list,
7210 trw_layer_analyse_close );
7213 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7215 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7216 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7218 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7221 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7223 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7224 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7227 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7228 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7232 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7234 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7235 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7238 if ( !strncmp(wp->comment, "http", 4) ) {
7239 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7240 } else if ( !strncmp(wp->description, "http", 4) ) {
7241 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7245 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7247 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7249 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7251 // No actual change to the name supplied
7253 if (strcmp(newname, wp->name) == 0 )
7256 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7259 // An existing waypoint has been found with the requested name
7260 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7261 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7266 // Update WP name and refresh the treeview
7267 vik_waypoint_set_name (wp, newname);
7269 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7270 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7272 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7277 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7279 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7281 // No actual change to the name supplied
7283 if (strcmp(newname, trk->name) == 0)
7286 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7289 // An existing track has been found with the requested name
7290 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7291 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7295 // Update track name and refresh GUI parts
7296 vik_track_set_name (trk, newname);
7298 // Update any subwindows that could be displaying this track which has changed name
7299 // Only one Track Edit Window
7300 if ( l->current_tp_track == trk && l->tpwin ) {
7301 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7303 // Property Dialog of the track
7304 vik_trw_layer_propwin_update ( trk );
7306 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7307 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7309 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7314 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7316 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7318 // No actual change to the name supplied
7320 if (strcmp(newname, trk->name) == 0)
7323 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7326 // An existing track has been found with the requested name
7327 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7328 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7332 // Update track name and refresh GUI parts
7333 vik_track_set_name (trk, newname);
7335 // Update any subwindows that could be displaying this track which has changed name
7336 // Only one Track Edit Window
7337 if ( l->current_tp_track == trk && l->tpwin ) {
7338 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7340 // Property Dialog of the track
7341 vik_trw_layer_propwin_update ( trk );
7343 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7344 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7346 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7353 static gboolean is_valid_geocache_name ( gchar *str )
7355 gint len = strlen ( str );
7356 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]));
7359 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7361 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7362 a_acquire_set_filter_track ( trk );
7365 #ifdef VIK_CONFIG_GOOGLE
7366 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7368 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7369 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7372 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7374 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7376 gchar *escaped = uri_escape ( tr->comment );
7377 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7378 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7385 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7386 /* viewpoint is now available instead */
7387 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7389 static menu_array_sublayer pass_along;
7391 gboolean rv = FALSE;
7393 pass_along[MA_VTL] = l;
7394 pass_along[MA_VLP] = vlp;
7395 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7396 pass_along[MA_SUBLAYER_ID] = sublayer;
7397 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7398 pass_along[MA_VVP] = vvp;
7399 pass_along[MA_TV_ITER] = iter;
7400 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7402 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7406 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7408 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7409 gtk_widget_show ( item );
7411 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7412 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7413 if (tr && tr->property_dialog)
7414 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7416 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7417 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7418 if (tr && tr->property_dialog)
7419 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7422 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7424 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7425 gtk_widget_show ( item );
7427 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7428 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7429 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7430 gtk_widget_show ( item );
7432 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7434 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7435 gtk_widget_show ( item );
7437 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7439 // Always create separator as now there is always at least the transform menu option
7440 item = gtk_menu_item_new ();
7441 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7442 gtk_widget_show ( item );
7444 /* could be a right-click using the tool */
7445 if ( vlp != NULL ) {
7446 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7447 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7449 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7450 gtk_widget_show ( item );
7453 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7455 if ( wp && wp->name ) {
7456 if ( is_valid_geocache_name ( wp->name ) ) {
7457 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7458 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7459 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7460 gtk_widget_show ( item );
7462 #ifdef VIK_CONFIG_GEOTAG
7463 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7464 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7465 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7466 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7467 gtk_widget_show ( item );
7471 if ( wp && wp->image )
7473 // Set up image paramater
7474 pass_along[MA_MISC] = wp->image;
7476 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7477 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
7478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7479 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7480 gtk_widget_show ( item );
7482 #ifdef VIK_CONFIG_GEOTAG
7483 GtkWidget *geotag_submenu = gtk_menu_new ();
7484 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7485 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7486 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7487 gtk_widget_show ( item );
7488 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7490 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7491 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7492 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7493 gtk_widget_show ( item );
7495 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7496 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7497 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7498 gtk_widget_show ( item );
7504 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7505 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7506 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7507 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7509 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7510 gtk_widget_show ( item );
7516 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7517 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7519 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7520 gtk_widget_show ( item );
7521 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7522 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7523 gtk_widget_set_sensitive ( item, TRUE );
7525 gtk_widget_set_sensitive ( item, FALSE );
7528 item = gtk_menu_item_new ();
7529 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7530 gtk_widget_show ( item );
7533 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7536 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7537 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7538 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7539 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7540 gtk_widget_show ( item );
7543 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7545 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7546 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7547 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7548 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7549 gtk_widget_show ( item );
7551 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7552 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7553 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7554 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7555 gtk_widget_show ( item );
7557 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7561 gtk_widget_show ( item );
7563 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7566 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7567 gtk_widget_show ( item );
7569 GtkWidget *vis_submenu = gtk_menu_new ();
7570 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7571 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7572 gtk_widget_show ( item );
7573 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7575 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7576 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7578 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7579 gtk_widget_show ( item );
7581 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7582 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7585 gtk_widget_show ( item );
7587 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7588 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7591 gtk_widget_show ( item );
7593 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7594 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7595 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7596 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7599 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7603 if ( l->current_track && !l->current_track->is_route ) {
7604 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7605 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7606 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7607 gtk_widget_show ( item );
7609 item = gtk_menu_item_new ();
7610 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7611 gtk_widget_show ( item );
7614 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7615 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7616 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7617 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7618 gtk_widget_show ( item );
7620 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7623 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7624 gtk_widget_show ( item );
7625 // Make it available only when a new track *not* already in progress
7626 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7628 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7629 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7630 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7631 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7632 gtk_widget_show ( item );
7634 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7635 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7636 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7637 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7638 gtk_widget_show ( item );
7640 GtkWidget *vis_submenu = gtk_menu_new ();
7641 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7642 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7643 gtk_widget_show ( item );
7644 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7646 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7647 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7648 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7649 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7650 gtk_widget_show ( item );
7652 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7653 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7656 gtk_widget_show ( item );
7658 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7659 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7663 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7664 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7665 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7666 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7667 gtk_widget_show ( item );
7669 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7670 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7671 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7672 gtk_widget_show ( item );
7675 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7679 if ( l->current_track && l->current_track->is_route ) {
7680 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7681 // Reuse finish track method
7682 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7683 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7684 gtk_widget_show ( item );
7686 item = gtk_menu_item_new ();
7687 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7688 gtk_widget_show ( item );
7691 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7692 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7694 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7695 gtk_widget_show ( item );
7697 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7698 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7700 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7701 gtk_widget_show ( item );
7702 // Make it available only when a new track *not* already in progress
7703 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7705 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7706 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7707 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7708 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7709 gtk_widget_show ( item );
7711 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7712 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7713 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7714 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7715 gtk_widget_show ( item );
7717 GtkWidget *vis_submenu = gtk_menu_new ();
7718 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720 gtk_widget_show ( item );
7721 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7727 gtk_widget_show ( item );
7729 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7730 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7731 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7732 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7733 gtk_widget_show ( item );
7735 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7736 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7740 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7741 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7743 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7745 gtk_widget_show ( item );
7747 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7748 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7749 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7750 gtk_widget_show ( item );
7754 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7755 GtkWidget *submenu_sort = gtk_menu_new ();
7756 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7757 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7758 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7759 gtk_widget_show ( item );
7760 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7762 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7763 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7764 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7765 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7766 gtk_widget_show ( item );
7768 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7769 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7771 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7772 gtk_widget_show ( item );
7775 GtkWidget *upload_submenu = gtk_menu_new ();
7777 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7779 item = gtk_menu_item_new ();
7780 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7781 gtk_widget_show ( item );
7783 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7784 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7785 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7786 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7787 if ( l->current_track ) {
7788 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7789 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7790 gtk_widget_show ( item );
7793 item = gtk_menu_item_new ();
7794 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7795 gtk_widget_show ( item );
7798 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7799 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7801 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7802 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7803 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7804 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7805 gtk_widget_show ( item );
7807 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7809 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7810 gtk_widget_show ( item );
7812 GtkWidget *goto_submenu;
7813 goto_submenu = gtk_menu_new ();
7814 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7816 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7817 gtk_widget_show ( item );
7818 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7820 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7823 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7824 gtk_widget_show ( item );
7826 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7827 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7828 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7829 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7830 gtk_widget_show ( item );
7832 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7833 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7834 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7835 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7836 gtk_widget_show ( item );
7838 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7839 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7840 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7841 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7842 gtk_widget_show ( item );
7844 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7845 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7846 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7847 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7848 gtk_widget_show ( item );
7850 // Routes don't have speeds
7851 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7852 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7853 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7854 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7855 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7856 gtk_widget_show ( item );
7859 GtkWidget *combine_submenu;
7860 combine_submenu = gtk_menu_new ();
7861 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7862 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7863 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7864 gtk_widget_show ( item );
7865 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7867 // Routes don't have times or segments...
7868 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7869 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7870 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7871 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7872 gtk_widget_show ( item );
7874 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7875 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7876 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7877 gtk_widget_show ( item );
7880 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7882 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7883 gtk_widget_show ( item );
7885 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7886 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7888 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7889 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7890 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7891 gtk_widget_show ( item );
7893 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7894 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7896 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7898 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7899 gtk_widget_show ( item );
7901 GtkWidget *split_submenu;
7902 split_submenu = gtk_menu_new ();
7903 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7904 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7905 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7906 gtk_widget_show ( item );
7907 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7909 // Routes don't have times or segments...
7910 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7911 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7912 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7913 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7914 gtk_widget_show ( item );
7916 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7917 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7919 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7920 gtk_widget_show ( item );
7923 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7925 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7926 gtk_widget_show ( item );
7928 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7930 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7931 gtk_widget_show ( item );
7932 // Make it available only when a trackpoint is selected.
7933 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7935 GtkWidget *insert_submenu = gtk_menu_new ();
7936 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7937 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7938 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7939 gtk_widget_show ( item );
7940 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7942 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7944 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7945 gtk_widget_show ( item );
7946 // Make it available only when a point is selected
7947 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7949 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7951 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7952 gtk_widget_show ( item );
7953 // Make it available only when a point is selected
7954 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7956 GtkWidget *delete_submenu;
7957 delete_submenu = gtk_menu_new ();
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7959 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7961 gtk_widget_show ( item );
7962 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7964 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7965 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7966 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7967 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7968 gtk_widget_show ( item );
7969 // Make it available only when a point is selected
7970 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7972 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7974 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7975 gtk_widget_show ( item );
7977 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7979 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7980 gtk_widget_show ( item );
7982 GtkWidget *transform_submenu;
7983 transform_submenu = gtk_menu_new ();
7984 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7985 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7986 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7987 gtk_widget_show ( item );
7988 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7990 GtkWidget *dem_submenu;
7991 dem_submenu = gtk_menu_new ();
7992 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7993 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
7994 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7995 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7999 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8000 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8001 gtk_widget_show ( item );
8003 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8005 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8006 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8007 gtk_widget_show ( item );
8009 GtkWidget *smooth_submenu;
8010 smooth_submenu = gtk_menu_new ();
8011 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8012 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8013 gtk_widget_show ( item );
8014 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8016 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8018 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8019 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8020 gtk_widget_show ( item );
8022 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8024 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8025 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8026 gtk_widget_show ( item );
8028 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8029 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8031 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8032 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8034 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8035 gtk_widget_show ( item );
8037 // Routes don't have timestamps - so this is only available for tracks
8038 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8039 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8041 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8042 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8043 gtk_widget_show ( item );
8046 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8047 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8053 gtk_widget_show ( item );
8055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8056 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8057 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8059 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8060 gtk_widget_show ( item );
8063 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8065 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8066 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8068 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8069 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
8070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8071 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8072 gtk_widget_show ( item );
8075 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8076 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8078 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8079 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8081 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8082 gtk_widget_show ( item );
8084 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8085 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8087 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8090 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8091 gtk_widget_show ( item );
8093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8094 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8095 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
8096 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8097 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8098 gtk_widget_show ( item );
8101 // ATM can't upload a single waypoint but can do waypoints to a GPS
8102 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8105 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8106 gtk_widget_show ( item );
8107 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8109 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8110 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8111 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8112 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8113 gtk_widget_show ( item );
8117 #ifdef VIK_CONFIG_GOOGLE
8118 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8120 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8121 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8123 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8124 gtk_widget_show ( item );
8128 // Some things aren't usable with routes
8129 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8130 #ifdef VIK_CONFIG_OPENSTREETMAP
8131 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8132 // Convert internal pointer into track
8133 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8136 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8137 gtk_widget_show ( item );
8140 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8143 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8144 gtk_widget_show ( item );
8146 /* ATM This function is only available via the layers panel, due to needing a vlp */
8148 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8149 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8150 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8152 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8153 gtk_widget_show ( item );
8157 #ifdef VIK_CONFIG_GEOTAG
8158 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8159 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8160 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8161 gtk_widget_show ( item );
8165 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8166 // Only show on viewport popmenu when a trackpoint is selected
8167 if ( ! vlp && l->current_tpl ) {
8169 item = gtk_menu_item_new ();
8170 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8171 gtk_widget_show ( item );
8173 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8174 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8176 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8177 gtk_widget_show ( item );
8181 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8182 GtkWidget *transform_submenu;
8183 transform_submenu = gtk_menu_new ();
8184 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8185 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8186 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8187 gtk_widget_show ( item );
8188 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8190 GtkWidget *dem_submenu;
8191 dem_submenu = gtk_menu_new ();
8192 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8193 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
8194 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8195 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8197 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8199 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8200 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8201 gtk_widget_show ( item );
8203 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8205 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8206 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8207 gtk_widget_show ( item );
8210 gtk_widget_show_all ( GTK_WIDGET(menu) );
8215 // TODO: Probably better to rework this track manipulation in viktrack.c
8216 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8219 if (!vtl->current_tpl)
8222 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8223 VikTrackpoint *tp_other = NULL;
8226 if (!vtl->current_tpl->prev)
8228 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8230 if (!vtl->current_tpl->next)
8232 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8235 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8238 VikTrackpoint *tp_new = vik_trackpoint_new();
8239 struct LatLon ll_current, ll_other;
8240 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8241 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8243 /* main positional interpolation */
8244 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8245 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8247 /* Now other properties that can be interpolated */
8248 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8250 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8251 /* Note here the division is applied to each part, then added
8252 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8253 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8254 tp_new->has_timestamp = TRUE;
8257 if (tp_current->speed != NAN && tp_other->speed != NAN)
8258 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8260 /* TODO - improve interpolation of course, as it may not be correct.
8261 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8262 [similar applies if value is in radians] */
8263 if (tp_current->course != NAN && tp_other->course != NAN)
8264 tp_new->course = (tp_current->course + tp_other->course)/2;
8266 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8268 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8269 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8271 // Otherwise try routes
8272 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8276 gint index = g_list_index ( trk->trackpoints, tp_current );
8280 // NB no recalculation of bounds since it is inserted between points
8281 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8286 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8292 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8296 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8298 if ( vtl->current_tpl )
8300 vtl->current_tpl = NULL;
8301 vtl->current_tp_track = NULL;
8302 vtl->current_tp_id = NULL;
8303 vik_layer_emit_update(VIK_LAYER(vtl));
8307 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8309 g_assert ( vtl->tpwin != NULL );
8310 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8311 trw_layer_cancel_current_tp ( vtl, TRUE );
8313 if ( vtl->current_tpl == NULL )
8316 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8318 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8319 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8321 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8323 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8325 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8329 trw_layer_trackpoint_selected_delete ( vtl, tr );
8331 if ( vtl->current_tpl )
8332 // Reset dialog with the available adjacent trackpoint
8333 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8335 vik_layer_emit_update(VIK_LAYER(vtl));
8337 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8339 if ( vtl->current_tp_track )
8340 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8341 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8343 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8345 if ( vtl->current_tp_track )
8346 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8347 vik_layer_emit_update(VIK_LAYER(vtl));
8349 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8351 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8352 vik_layer_emit_update(VIK_LAYER(vtl));
8354 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8355 vik_layer_emit_update(VIK_LAYER(vtl));
8359 * trw_layer_dialog_shift:
8360 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8362 * Try to reposition a dialog if it's over the specified coord
8363 * so to not obscure the item of interest
8365 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8367 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8369 // Attempt force dialog to be shown so we can find out where it is more reliably...
8370 while ( gtk_events_pending() )
8371 gtk_main_iteration ();
8373 // get parent window position & size
8374 gint win_pos_x, win_pos_y;
8375 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8377 gint win_size_x, win_size_y;
8378 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8380 // get own dialog size
8381 gint dia_size_x, dia_size_y;
8382 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8384 // get own dialog position
8385 gint dia_pos_x, dia_pos_y;
8386 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8388 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8389 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8391 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8393 gint vp_xx, vp_yy; // In viewport pixels
8394 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8396 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8400 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8402 // Transform Viewport pixels into absolute pixels
8403 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8404 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8406 // Is dialog over the point (to within an ^^ edge value)
8407 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8408 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8412 gint hh = vik_viewport_get_height ( vvp );
8414 // Consider the difference in viewport to the full window
8415 gint offset_y = dest_y;
8416 // Add difference between dialog and window sizes
8417 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8419 if ( vp_yy > hh/2 ) {
8420 // Point in bottom half, move window to top half
8421 gtk_window_move ( dialog, dia_pos_x, offset_y );
8424 // Point in top half, move dialog down
8425 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8429 // Shift left<->right
8430 gint ww = vik_viewport_get_width ( vvp );
8432 // Consider the difference in viewport to the full window
8433 gint offset_x = dest_x;
8434 // Add difference between dialog and window sizes
8435 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8437 if ( vp_xx > ww/2 ) {
8438 // Point on right, move window to left
8439 gtk_window_move ( dialog, offset_x, dia_pos_y );
8442 // Point on left, move right
8443 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8451 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8455 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8456 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8457 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8458 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8460 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8462 if ( vtl->current_tpl ) {
8463 // get tp pixel position
8464 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8466 // Shift up<->down to try not to obscure the trackpoint.
8467 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8471 if ( vtl->current_tpl )
8472 if ( vtl->current_tp_track )
8473 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8474 /* set layer name and TP data */
8477 /***************************************************************************
8479 ***************************************************************************/
8481 /*** Utility data structures and functions ****/
8485 gint closest_x, closest_y;
8486 gboolean draw_images;
8487 gpointer *closest_wp_id;
8488 VikWaypoint *closest_wp;
8494 gint closest_x, closest_y;
8495 gpointer closest_track_id;
8496 VikTrackpoint *closest_tp;
8502 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8508 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8510 // If waypoint has an image then use the image size to select
8511 if ( params->draw_images && wp->image ) {
8512 gint slackx, slacky;
8513 slackx = wp->image_width / 2;
8514 slacky = wp->image_height / 2;
8516 if ( x <= params->x + slackx && x >= params->x - slackx
8517 && y <= params->y + slacky && y >= params->y - slacky ) {
8518 params->closest_wp_id = id;
8519 params->closest_wp = wp;
8520 params->closest_x = x;
8521 params->closest_y = y;
8524 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8525 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8526 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8528 params->closest_wp_id = id;
8529 params->closest_wp = wp;
8530 params->closest_x = x;
8531 params->closest_y = y;
8535 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8537 GList *tpl = t->trackpoints;
8543 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8549 tp = VIK_TRACKPOINT(tpl->data);
8551 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8553 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8554 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8555 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8557 params->closest_track_id = id;
8558 params->closest_tp = tp;
8559 params->closest_tpl = tpl;
8560 params->closest_x = x;
8561 params->closest_y = y;
8567 // ATM: Leave this as 'Track' only.
8568 // Not overly bothered about having a snap to route trackpoint capability
8569 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8571 TPSearchParams params;
8575 params.closest_track_id = NULL;
8576 params.closest_tp = NULL;
8577 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8578 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8579 return params.closest_tp;
8582 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8584 WPSearchParams params;
8588 params.draw_images = vtl->drawimages;
8589 params.closest_wp = NULL;
8590 params.closest_wp_id = NULL;
8591 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8592 return params.closest_wp;
8596 // Some forward declarations
8597 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8598 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8599 static void marker_end_move ( tool_ed_t *t );
8602 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8606 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8608 // Here always allow snapping back to the original location
8609 // this is useful when one decides not to move the thing afterall
8610 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8613 if ( event->state & GDK_CONTROL_MASK )
8615 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8617 new_coord = tp->coord;
8621 if ( event->state & GDK_SHIFT_MASK )
8623 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8625 new_coord = wp->coord;
8629 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8631 marker_moveto ( t, x, y );
8638 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8640 if ( t->holding && event->button == 1 )
8643 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8646 if ( event->state & GDK_CONTROL_MASK )
8648 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8650 new_coord = tp->coord;
8654 if ( event->state & GDK_SHIFT_MASK )
8656 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8658 new_coord = wp->coord;
8661 marker_end_move ( t );
8663 // Determine if working on a waypoint or a trackpoint
8664 if ( t->is_waypoint ) {
8665 // Update waypoint position
8666 vtl->current_wp->coord = new_coord;
8667 trw_layer_calculate_bounds_waypoints ( vtl );
8668 // Reset waypoint pointer
8669 vtl->current_wp = NULL;
8670 vtl->current_wp_id = NULL;
8673 if ( vtl->current_tpl ) {
8674 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8676 if ( vtl->current_tp_track )
8677 vik_track_calculate_bounds ( vtl->current_tp_track );
8680 if ( vtl->current_tp_track )
8681 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8682 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8686 vik_layer_emit_update ( VIK_LAYER(vtl) );
8693 Returns true if a waypoint or track is found near the requested event position for this particular layer
8694 The item found is automatically selected
8695 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8697 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8699 if ( event->button != 1 )
8702 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8705 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8709 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8711 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8713 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8714 WPSearchParams wp_params;
8715 wp_params.vvp = vvp;
8716 wp_params.x = event->x;
8717 wp_params.y = event->y;
8718 wp_params.draw_images = vtl->drawimages;
8719 wp_params.closest_wp_id = NULL;
8720 wp_params.closest_wp = NULL;
8722 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8724 if ( wp_params.closest_wp ) {
8727 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8729 // Too easy to move it so must be holding shift to start immediately moving it
8730 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8731 if ( event->state & GDK_SHIFT_MASK ||
8732 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8733 // Put into 'move buffer'
8734 // NB vvp & vw already set in tet
8735 tet->vtl = (gpointer)vtl;
8736 tet->is_waypoint = TRUE;
8738 marker_begin_move (tet, event->x, event->y);
8741 vtl->current_wp = wp_params.closest_wp;
8742 vtl->current_wp_id = wp_params.closest_wp_id;
8744 if ( event->type == GDK_2BUTTON_PRESS ) {
8745 if ( vtl->current_wp->image ) {
8746 menu_array_sublayer values;
8747 values[MA_VTL] = vtl;
8748 values[MA_MISC] = vtl->current_wp->image;
8749 trw_layer_show_picture ( values );
8753 vik_layer_emit_update ( VIK_LAYER(vtl) );
8759 // Used for both track and route lists
8760 TPSearchParams tp_params;
8761 tp_params.vvp = vvp;
8762 tp_params.x = event->x;
8763 tp_params.y = event->y;
8764 tp_params.closest_track_id = NULL;
8765 tp_params.closest_tp = NULL;
8766 tp_params.closest_tpl = NULL;
8767 tp_params.bbox = bbox;
8769 if (vtl->tracks_visible) {
8770 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8772 if ( tp_params.closest_tp ) {
8774 // Always select + highlight the track
8775 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8777 tet->is_waypoint = FALSE;
8779 // Select the Trackpoint
8780 // Can move it immediately when control held or it's the previously selected tp
8781 if ( event->state & GDK_CONTROL_MASK ||
8782 vtl->current_tpl == tp_params.closest_tpl ) {
8783 // Put into 'move buffer'
8784 // NB vvp & vw already set in tet
8785 tet->vtl = (gpointer)vtl;
8786 marker_begin_move (tet, event->x, event->y);
8789 vtl->current_tpl = tp_params.closest_tpl;
8790 vtl->current_tp_id = tp_params.closest_track_id;
8791 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8793 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8796 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8798 vik_layer_emit_update ( VIK_LAYER(vtl) );
8803 // Try again for routes
8804 if (vtl->routes_visible) {
8805 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8807 if ( tp_params.closest_tp ) {
8809 // Always select + highlight the track
8810 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8812 tet->is_waypoint = FALSE;
8814 // Select the Trackpoint
8815 // Can move it immediately when control held or it's the previously selected tp
8816 if ( event->state & GDK_CONTROL_MASK ||
8817 vtl->current_tpl == tp_params.closest_tpl ) {
8818 // Put into 'move buffer'
8819 // NB vvp & vw already set in tet
8820 tet->vtl = (gpointer)vtl;
8821 marker_begin_move (tet, event->x, event->y);
8824 vtl->current_tpl = tp_params.closest_tpl;
8825 vtl->current_tp_id = tp_params.closest_track_id;
8826 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8828 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8831 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8833 vik_layer_emit_update ( VIK_LAYER(vtl) );
8838 /* these aren't the droids you're looking for */
8839 vtl->current_wp = NULL;
8840 vtl->current_wp_id = NULL;
8841 trw_layer_cancel_current_tp ( vtl, FALSE );
8844 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8849 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8851 if ( event->button != 3 )
8854 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8857 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8860 /* Post menu for the currently selected item */
8862 /* See if a track is selected */
8863 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8864 if ( track && track->visible ) {
8866 if ( track->name ) {
8868 if ( vtl->track_right_click_menu )
8869 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8871 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8878 if ( track->is_route )
8879 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8881 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8883 if ( trkf && udataU.uuid ) {
8886 if ( track->is_route )
8887 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8889 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8891 trw_layer_sublayer_add_menu_items ( vtl,
8892 vtl->track_right_click_menu,
8894 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8900 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8906 /* See if a waypoint is selected */
8907 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8908 if ( waypoint && waypoint->visible ) {
8909 if ( waypoint->name ) {
8911 if ( vtl->wp_right_click_menu )
8912 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8914 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8917 udata.wp = waypoint;
8920 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8922 if ( wpf && udata.uuid ) {
8923 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8925 trw_layer_sublayer_add_menu_items ( vtl,
8926 vtl->wp_right_click_menu,
8928 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8933 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8942 /* background drawing hook, to be passed the viewport */
8943 static gboolean tool_sync_done = TRUE;
8945 static gboolean tool_sync(gpointer data)
8947 VikViewport *vvp = data;
8948 gdk_threads_enter();
8949 vik_viewport_sync(vvp);
8950 tool_sync_done = TRUE;
8951 gdk_threads_leave();
8955 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8958 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8959 gdk_gc_set_function ( t->gc, GDK_INVERT );
8960 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8961 vik_viewport_sync(t->vvp);
8966 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8968 VikViewport *vvp = t->vvp;
8969 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8970 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8974 if (tool_sync_done) {
8975 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8976 tool_sync_done = FALSE;
8980 static void marker_end_move ( tool_ed_t *t )
8982 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8983 g_object_unref ( t->gc );
8987 /*** Edit waypoint ****/
8989 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8991 tool_ed_t *t = g_new(tool_ed_t, 1);
8997 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9002 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9004 WPSearchParams params;
9005 tool_ed_t *t = data;
9006 VikViewport *vvp = t->vvp;
9008 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9015 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9018 if ( vtl->current_wp && vtl->current_wp->visible )
9020 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9022 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9024 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9025 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9027 if ( event->button == 3 )
9028 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9030 marker_begin_move(t, event->x, event->y);
9037 params.x = event->x;
9038 params.y = event->y;
9039 params.draw_images = vtl->drawimages;
9040 params.closest_wp_id = NULL;
9041 params.closest_wp = NULL;
9042 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9043 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9045 marker_begin_move(t, event->x, event->y);
9048 else if ( params.closest_wp )
9050 if ( event->button == 3 )
9051 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9053 vtl->waypoint_rightclick = FALSE;
9055 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9057 vtl->current_wp = params.closest_wp;
9058 vtl->current_wp_id = params.closest_wp_id;
9060 /* could make it so don't update if old WP is off screen and new is null but oh well */
9061 vik_layer_emit_update ( VIK_LAYER(vtl) );
9065 vtl->current_wp = NULL;
9066 vtl->current_wp_id = NULL;
9067 vtl->waypoint_rightclick = FALSE;
9068 vik_layer_emit_update ( VIK_LAYER(vtl) );
9072 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9074 tool_ed_t *t = data;
9075 VikViewport *vvp = t->vvp;
9077 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9082 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9085 if ( event->state & GDK_CONTROL_MASK )
9087 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9089 new_coord = tp->coord;
9093 if ( event->state & GDK_SHIFT_MASK )
9095 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9096 if ( wp && wp != vtl->current_wp )
9097 new_coord = wp->coord;
9102 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9104 marker_moveto ( t, x, y );
9111 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9113 tool_ed_t *t = data;
9114 VikViewport *vvp = t->vvp;
9116 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9119 if ( t->holding && event->button == 1 )
9122 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9125 if ( event->state & GDK_CONTROL_MASK )
9127 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9129 new_coord = tp->coord;
9133 if ( event->state & GDK_SHIFT_MASK )
9135 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9136 if ( wp && wp != vtl->current_wp )
9137 new_coord = wp->coord;
9140 marker_end_move ( t );
9142 vtl->current_wp->coord = new_coord;
9144 trw_layer_calculate_bounds_waypoints ( vtl );
9145 vik_layer_emit_update ( VIK_LAYER(vtl) );
9148 /* PUT IN RIGHT PLACE!!! */
9149 if ( event->button == 3 && vtl->waypoint_rightclick )
9151 if ( vtl->wp_right_click_menu )
9152 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9153 if ( vtl->current_wp ) {
9154 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9155 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 );
9156 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9158 vtl->waypoint_rightclick = FALSE;
9163 /*** New track ****/
9165 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9172 GdkDrawable *drawable;
9178 * Draw specified pixmap
9180 static gboolean draw_sync ( gpointer data )
9182 draw_sync_t *ds = (draw_sync_t*) data;
9183 // Sometimes don't want to draw
9184 // normally because another update has taken precedent such as panning the display
9185 // which means this pixmap is no longer valid
9186 if ( ds->vtl->draw_sync_do ) {
9187 gdk_threads_enter();
9188 gdk_draw_drawable (ds->drawable,
9191 0, 0, 0, 0, -1, -1);
9192 ds->vtl->draw_sync_done = TRUE;
9193 gdk_threads_leave();
9199 static gchar* distance_string (gdouble distance)
9203 /* draw label with distance */
9204 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9205 switch (dist_units) {
9206 case VIK_UNITS_DISTANCE_MILES:
9207 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9208 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9209 } else if (distance < 1609.4) {
9210 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9212 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9216 // VIK_UNITS_DISTANCE_KILOMETRES
9217 if (distance >= 1000 && distance < 100000) {
9218 g_sprintf(str, "%3.2f km", distance/1000.0);
9219 } else if (distance < 1000) {
9220 g_sprintf(str, "%d m", (int)distance);
9222 g_sprintf(str, "%d km", (int)distance/1000);
9226 return g_strdup (str);
9230 * Actually set the message in statusbar
9232 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9234 // Only show elevation data when track has some elevation properties
9235 gchar str_gain_loss[64];
9236 str_gain_loss[0] = '\0';
9237 gchar str_last_step[64];
9238 str_last_step[0] = '\0';
9239 gchar *str_total = distance_string (distance);
9241 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9242 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9243 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9245 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9248 if ( last_step > 0 ) {
9249 gchar *tmp = distance_string (last_step);
9250 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9254 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9256 // Write with full gain/loss information
9257 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9258 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9260 g_free ( str_total );
9264 * Figure out what information should be set in the statusbar and then write it
9266 static void update_statusbar ( VikTrwLayer *vtl )
9268 // Get elevation data
9269 gdouble elev_gain, elev_loss;
9270 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9272 /* Find out actual distance of current track */
9273 gdouble distance = vik_track_get_length (vtl->current_track);
9275 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9279 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9281 /* if we haven't sync'ed yet, we don't have time to do more. */
9282 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9283 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9285 static GdkPixmap *pixmap = NULL;
9287 // Need to check in case window has been resized
9288 w1 = vik_viewport_get_width(vvp);
9289 h1 = vik_viewport_get_height(vvp);
9291 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9293 gdk_drawable_get_size (pixmap, &w2, &h2);
9294 if (w1 != w2 || h1 != h2) {
9295 g_object_unref ( G_OBJECT ( pixmap ) );
9296 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9299 // Reset to background
9300 gdk_draw_drawable (pixmap,
9301 vtl->current_track_newpoint_gc,
9302 vik_viewport_get_pixmap(vvp),
9303 0, 0, 0, 0, -1, -1);
9305 draw_sync_t *passalong;
9308 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9310 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9311 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9312 // thus when we come to reset to the background it would include what we have already drawn!!
9313 gdk_draw_line ( pixmap,
9314 vtl->current_track_newpoint_gc,
9315 x1, y1, event->x, event->y );
9316 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9318 /* Find out actual distance of current track */
9319 gdouble distance = vik_track_get_length (vtl->current_track);
9321 // Now add distance to where the pointer is //
9324 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9325 vik_coord_to_latlon ( &coord, &ll );
9326 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9327 distance = distance + last_step;
9329 // Get elevation data
9330 gdouble elev_gain, elev_loss;
9331 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9333 // Adjust elevation data (if available) for the current pointer position
9335 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9336 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9337 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9338 // Adjust elevation of last track point
9339 if ( elev_new > last_tpt->altitude )
9341 elev_gain += elev_new - last_tpt->altitude;
9344 elev_loss += last_tpt->altitude - elev_new;
9349 // Display of the distance 'tooltip' during track creation is controlled by a preference
9351 if ( a_vik_get_create_track_tooltip() ) {
9353 gchar *str = distance_string (distance);
9355 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9356 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9357 pango_layout_set_text (pl, str, -1);
9359 pango_layout_get_pixel_size ( pl, &wd, &hd );
9362 // offset from cursor a bit depending on font size
9366 // Create a background block to make the text easier to read over the background map
9367 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9368 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9369 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9371 g_object_unref ( G_OBJECT ( pl ) );
9372 g_object_unref ( G_OBJECT ( background_block_gc ) );
9376 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9377 passalong->vtl = vtl;
9378 passalong->pixmap = pixmap;
9379 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9380 passalong->gc = vtl->current_track_newpoint_gc;
9384 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9386 // Update statusbar with full gain/loss information
9387 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9389 // draw pixmap when we have time to
9390 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9391 vtl->draw_sync_done = FALSE;
9392 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9394 return VIK_LAYER_TOOL_ACK;
9397 // NB vtl->current_track must be valid
9398 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9401 if ( vtl->current_track->trackpoints ) {
9402 // TODO rework this...
9403 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9404 GList *last = g_list_last(vtl->current_track->trackpoints);
9405 g_free ( last->data );
9406 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9408 vik_track_calculate_bounds ( vtl->current_track );
9412 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9414 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9415 vtl->current_track = NULL;
9416 vik_layer_emit_update ( VIK_LAYER(vtl) );
9418 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9419 undo_trackpoint_add ( vtl );
9420 update_statusbar ( vtl );
9421 vik_layer_emit_update ( VIK_LAYER(vtl) );
9428 * Common function to handle trackpoint button requests on either a route or a track
9429 * . enables adding a point via normal click
9430 * . enables removal of last point via right click
9431 * . finishing of the track or route via double clicking
9433 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9437 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9440 if ( event->button == 2 ) {
9441 // As the display is panning, the new track pixmap is now invalid so don't draw it
9442 // otherwise this drawing done results in flickering back to an old image
9443 vtl->draw_sync_do = FALSE;
9447 if ( event->button == 3 )
9449 if ( !vtl->current_track )
9451 undo_trackpoint_add ( vtl );
9452 update_statusbar ( vtl );
9453 vik_layer_emit_update ( VIK_LAYER(vtl) );
9457 if ( event->type == GDK_2BUTTON_PRESS )
9459 /* subtract last (duplicate from double click) tp then end */
9460 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9462 /* undo last, then end */
9463 undo_trackpoint_add ( vtl );
9464 vtl->current_track = NULL;
9466 vik_layer_emit_update ( VIK_LAYER(vtl) );
9470 tp = vik_trackpoint_new();
9471 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9473 /* snap to other TP */
9474 if ( event->state & GDK_CONTROL_MASK )
9476 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9478 tp->coord = other_tp->coord;
9481 tp->newsegment = FALSE;
9482 tp->has_timestamp = FALSE;
9485 if ( vtl->current_track ) {
9486 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9487 /* Auto attempt to get elevation from DEM data (if it's available) */
9488 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9491 vtl->ct_x1 = vtl->ct_x2;
9492 vtl->ct_y1 = vtl->ct_y2;
9493 vtl->ct_x2 = event->x;
9494 vtl->ct_y2 = event->y;
9496 vik_layer_emit_update ( VIK_LAYER(vtl) );
9500 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9502 // ----------------------------------------------------- if current is a route - switch to new track
9503 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9505 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9506 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9508 new_track_create_common ( vtl, name );
9514 return tool_new_track_or_route_click ( vtl, event, vvp );
9517 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9519 if ( event->button == 2 ) {
9520 // Pan moving ended - enable potential point drawing again
9521 vtl->draw_sync_do = TRUE;
9522 vtl->draw_sync_done = TRUE;
9526 /*** New route ****/
9528 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9533 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9535 // -------------------------- if current is a track - switch to new route
9536 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9538 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9539 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9540 new_route_create_common ( vtl, name );
9546 return tool_new_track_or_route_click ( vtl, event, vvp );
9549 /*** New waypoint ****/
9551 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9556 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9559 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9561 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9562 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9563 trw_layer_calculate_bounds_waypoints ( vtl );
9564 vik_layer_emit_update ( VIK_LAYER(vtl) );
9570 /*** Edit trackpoint ****/
9572 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9574 tool_ed_t *t = g_new(tool_ed_t, 1);
9580 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9585 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9587 tool_ed_t *t = data;
9588 VikViewport *vvp = t->vvp;
9589 TPSearchParams params;
9590 /* OUTDATED DOCUMENTATION:
9591 find 5 pixel range on each side. then put these UTM, and a pointer
9592 to the winning track name (and maybe the winning track itself), and a
9593 pointer to the winning trackpoint, inside an array or struct. pass
9594 this along, do a foreach on the tracks which will do a foreach on the
9597 params.x = event->x;
9598 params.y = event->y;
9599 params.closest_track_id = NULL;
9600 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9601 params.closest_tp = NULL;
9602 params.closest_tpl = NULL;
9603 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9605 if ( event->button != 1 )
9608 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9611 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9614 if ( vtl->current_tpl )
9616 /* 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.) */
9617 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9618 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9623 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9625 if ( current_tr->visible &&
9626 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9627 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9628 marker_begin_move ( t, event->x, event->y );
9634 if ( vtl->tracks_visible )
9635 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9637 if ( params.closest_tp )
9639 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9640 vtl->current_tpl = params.closest_tpl;
9641 vtl->current_tp_id = params.closest_track_id;
9642 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9643 trw_layer_tpwin_init ( vtl );
9644 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9645 vik_layer_emit_update ( VIK_LAYER(vtl) );
9649 if ( vtl->routes_visible )
9650 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9652 if ( params.closest_tp )
9654 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9655 vtl->current_tpl = params.closest_tpl;
9656 vtl->current_tp_id = params.closest_track_id;
9657 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9658 trw_layer_tpwin_init ( vtl );
9659 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9660 vik_layer_emit_update ( VIK_LAYER(vtl) );
9664 /* these aren't the droids you're looking for */
9668 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9670 tool_ed_t *t = data;
9671 VikViewport *vvp = t->vvp;
9673 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9679 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9682 if ( event->state & GDK_CONTROL_MASK )
9684 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9685 if ( tp && tp != vtl->current_tpl->data )
9686 new_coord = tp->coord;
9688 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9691 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9692 marker_moveto ( t, x, y );
9700 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9702 tool_ed_t *t = data;
9703 VikViewport *vvp = t->vvp;
9705 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9707 if ( event->button != 1)
9712 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9715 if ( event->state & GDK_CONTROL_MASK )
9717 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9718 if ( tp && tp != vtl->current_tpl->data )
9719 new_coord = tp->coord;
9722 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9723 if ( vtl->current_tp_track )
9724 vik_track_calculate_bounds ( vtl->current_tp_track );
9726 marker_end_move ( t );
9728 /* diff dist is diff from orig */
9730 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9732 vik_layer_emit_update ( VIK_LAYER(vtl) );
9739 /*** Route Finder ***/
9740 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9745 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9748 if ( !vtl ) return FALSE;
9749 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9750 if ( event->button == 3 && vtl->route_finder_current_track ) {
9752 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9754 vtl->route_finder_coord = *new_end;
9756 vik_layer_emit_update ( VIK_LAYER(vtl) );
9757 /* remove last ' to:...' */
9758 if ( vtl->route_finder_current_track->comment ) {
9759 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9760 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9761 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9762 last_to - vtl->route_finder_current_track->comment - 1);
9763 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9768 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9769 struct LatLon start, end;
9771 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9772 vik_coord_to_latlon ( &(tmp), &end );
9773 vtl->route_finder_coord = tmp; /* for continuations */
9775 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9776 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9777 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9779 vtl->route_finder_check_added_track = TRUE;
9780 vtl->route_finder_started = FALSE;
9783 vik_routing_default_find ( vtl, start, end);
9785 /* see if anything was done -- a track was added or appended to */
9786 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9787 vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
9788 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9789 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9790 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9791 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9794 if ( vtl->route_finder_added_track )
9795 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9797 vtl->route_finder_added_track = NULL;
9798 vtl->route_finder_check_added_track = FALSE;
9799 vtl->route_finder_append = FALSE;
9801 vik_layer_emit_update ( VIK_LAYER(vtl) );
9803 vtl->route_finder_started = TRUE;
9804 vtl->route_finder_coord = tmp;
9805 vtl->route_finder_current_track = NULL;
9810 /*** Show picture ****/
9812 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9817 /* Params are: vvp, event, last match found or NULL */
9818 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9820 if ( wp->image && wp->visible )
9822 gint x, y, slackx, slacky;
9823 GdkEventButton *event = (GdkEventButton *) params[1];
9825 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9826 slackx = wp->image_width / 2;
9827 slacky = wp->image_height / 2;
9828 if ( x <= event->x + slackx && x >= event->x - slackx
9829 && y <= event->y + slacky && y >= event->y - slacky )
9831 params[2] = wp->image; /* we've found a match. however continue searching
9832 * since we want to find the last match -- that
9833 * is, the match that was drawn last. */
9838 static void trw_layer_show_picture ( menu_array_sublayer values )
9840 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9842 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9845 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9846 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9847 g_free ( quoted_file );
9848 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9850 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() );
9851 g_error_free ( err );
9854 #endif /* WINDOWS */
9857 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9859 gpointer params[3] = { vvp, event, NULL };
9860 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9862 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9865 static menu_array_sublayer values;
9866 values[MA_VTL] = vtl;
9867 values[MA_MISC] = params[2];
9868 trw_layer_show_picture ( values );
9869 return TRUE; /* found a match */
9872 return FALSE; /* go through other layers, searching for a match */
9875 /***************************************************************************
9877 ***************************************************************************/
9880 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9882 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9883 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9886 /* Structure for thumbnail creating data used in the background thread */
9888 VikTrwLayer *vtl; // Layer needed for redrawing
9889 GSList *pics; // Image list
9890 } thumbnail_create_thread_data;
9892 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9894 guint total = g_slist_length(tctd->pics), done = 0;
9895 while ( tctd->pics )
9897 a_thumbnails_create ( (gchar *) tctd->pics->data );
9898 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9900 return -1; /* Abort thread */
9902 tctd->pics = tctd->pics->next;
9905 // Redraw to show the thumbnails as they are now created
9906 if ( IS_VIK_LAYER(tctd->vtl) )
9907 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9912 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9914 while ( tctd->pics )
9916 g_free ( tctd->pics->data );
9917 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9922 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9924 if ( ! vtl->has_verified_thumbnails )
9926 GSList *pics = NULL;
9927 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9930 gint len = g_slist_length ( pics );
9931 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9932 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9935 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9937 (vik_thr_func) create_thumbnails_thread,
9939 (vik_thr_free_func) thumbnail_create_thread_free,
9947 static const gchar* my_track_colors ( gint ii )
9949 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9961 // Fast and reliable way of returning a colour
9962 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9965 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9967 GHashTableIter iter;
9968 gpointer key, value;
9972 g_hash_table_iter_init ( &iter, vtl->tracks );
9974 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9976 // Tracks get a random spread of colours if not already assigned
9977 if ( ! VIK_TRACK(value)->has_color ) {
9978 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9979 VIK_TRACK(value)->color = vtl->track_color;
9981 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9983 VIK_TRACK(value)->has_color = TRUE;
9986 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9989 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9995 g_hash_table_iter_init ( &iter, vtl->routes );
9997 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9999 // Routes get an intermix of reds
10000 if ( ! VIK_TRACK(value)->has_color ) {
10002 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10004 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10005 VIK_TRACK(value)->has_color = TRUE;
10008 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10015 * (Re)Calculate the bounds of the waypoints in this layer,
10016 * This should be called whenever waypoints are changed
10018 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10020 struct LatLon topleft = { 0.0, 0.0 };
10021 struct LatLon bottomright = { 0.0, 0.0 };
10024 GHashTableIter iter;
10025 gpointer key, value;
10027 g_hash_table_iter_init ( &iter, vtl->waypoints );
10029 // Set bounds to first point
10030 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10031 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10032 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10035 // Ensure there is another point...
10036 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10038 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10040 // See if this point increases the bounds.
10041 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10043 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10044 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10045 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10046 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10050 vtl->waypoints_bbox.north = topleft.lat;
10051 vtl->waypoints_bbox.east = bottomright.lon;
10052 vtl->waypoints_bbox.south = bottomright.lat;
10053 vtl->waypoints_bbox.west = topleft.lon;
10056 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10058 vik_track_calculate_bounds ( trk );
10061 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10063 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10064 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10067 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10069 if ( ! VIK_LAYER(vtl)->vt )
10072 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10073 if ( g_hash_table_size (vtl->tracks) > 1 )
10074 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10076 if ( g_hash_table_size (vtl->routes) > 1 )
10077 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10079 if ( g_hash_table_size (vtl->waypoints) > 1 )
10080 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10083 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10085 if ( VIK_LAYER(vtl)->realized )
10086 trw_layer_verify_thumbnails ( vtl, vvp );
10087 trw_layer_track_alloc_colors ( vtl );
10089 trw_layer_calculate_bounds_waypoints ( vtl );
10090 trw_layer_calculate_bounds_tracks ( vtl );
10092 // Apply treeview sort after loading all the tracks for this layer
10093 // (rather than sorted insert on each individual track additional)
10094 // and after subsequent changes to the properties as the specified order may have changed.
10095 // since the sorting of a treeview section is now very quick
10096 // NB sorting is also performed after every name change as well to maintain the list order
10097 trw_layer_sort_all ( vtl );
10099 // Setting metadata time if not otherwise set
10100 if ( vtl->metadata ) {
10102 gboolean need_to_set_time = TRUE;
10103 if ( vtl->metadata->timestamp ) {
10104 need_to_set_time = FALSE;
10105 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10106 need_to_set_time = TRUE;
10109 if ( need_to_set_time ) {
10110 // Could rewrite this as a general get first time of a TRW Layer function
10111 GTimeVal timestamp;
10112 timestamp.tv_usec = 0;
10113 gboolean has_timestamp = FALSE;
10116 gl = g_hash_table_get_values ( vtl->tracks );
10117 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10118 gl = g_list_first ( gl );
10120 // Check times of tracks
10122 // Only need to check the first track as they have been sorted by time
10123 VikTrack *trk = (VikTrack*)gl->data;
10124 // Assume trackpoints already sorted by time
10125 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10126 if ( tpt && tpt->has_timestamp ) {
10127 timestamp.tv_sec = tpt->timestamp;
10128 has_timestamp = TRUE;
10130 g_list_free ( gl );
10133 if ( !has_timestamp ) {
10134 // 'Last' resort - current time
10135 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10136 g_get_current_time ( ×tamp );
10138 // Check times of waypoints
10139 gl = g_hash_table_get_values ( vtl->waypoints );
10141 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10142 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10143 if ( wpt->has_timestamp ) {
10144 if ( timestamp.tv_sec > wpt->timestamp ) {
10145 timestamp.tv_sec = wpt->timestamp;
10146 has_timestamp = TRUE;
10150 g_list_free ( gl );
10153 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10158 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10160 return vtl->coord_mode;
10164 * Uniquify the whole layer
10165 * Also requires the layers panel as the names shown there need updating too
10166 * Returns whether the operation was successful or not
10168 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10170 if ( vtl && vlp ) {
10171 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10172 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10173 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10179 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10181 vik_coord_convert ( &(wp->coord), *dest_mode );
10184 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10186 vik_track_convert ( tr, *dest_mode );
10189 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10191 if ( vtl->coord_mode != dest_mode )
10193 vtl->coord_mode = dest_mode;
10194 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10195 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10196 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10200 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10202 vtl->menu_selection = selection;
10205 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10207 return (vtl->menu_selection);
10210 /* ----------- Downloading maps along tracks --------------- */
10212 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10214 /* TODO: calculating based on current size of viewport */
10215 const gdouble w_at_zoom_0_125 = 0.0013;
10216 const gdouble h_at_zoom_0_125 = 0.0011;
10217 gdouble zoom_factor = zoom_level/0.125;
10219 wh->lat = h_at_zoom_0_125 * zoom_factor;
10220 wh->lon = w_at_zoom_0_125 * zoom_factor;
10222 return 0; /* all OK */
10225 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10227 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10228 (dist->lat >= ABS(to->north_south - from->north_south)))
10231 VikCoord *coord = g_malloc(sizeof(VikCoord));
10232 coord->mode = VIK_COORD_LATLON;
10234 if (ABS(gradient) < 1) {
10235 if (from->east_west > to->east_west)
10236 coord->east_west = from->east_west - dist->lon;
10238 coord->east_west = from->east_west + dist->lon;
10239 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10241 if (from->north_south > to->north_south)
10242 coord->north_south = from->north_south - dist->lat;
10244 coord->north_south = from->north_south + dist->lat;
10245 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10251 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10253 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10254 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10256 VikCoord *next = from;
10258 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10260 list = g_list_prepend(list, next);
10266 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10268 typedef struct _Rect {
10273 #define GLRECT(iter) ((Rect *)((iter)->data))
10276 GList *rects_to_download = NULL;
10279 if (get_download_area_width(vvp, zoom_level, &wh))
10282 GList *iter = tr->trackpoints;
10286 gboolean new_map = TRUE;
10287 VikCoord *cur_coord, tl, br;
10290 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10292 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10293 rect = g_malloc(sizeof(Rect));
10296 rect->center = *cur_coord;
10297 rects_to_download = g_list_prepend(rects_to_download, rect);
10302 gboolean found = FALSE;
10303 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10304 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10315 GList *fillins = NULL;
10316 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10317 /* seems that ATM the function get_next_coord works only for LATLON */
10318 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10319 /* fill-ins for far apart points */
10320 GList *cur_rect, *next_rect;
10321 for (cur_rect = rects_to_download;
10322 (next_rect = cur_rect->next) != NULL;
10323 cur_rect = cur_rect->next) {
10324 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10325 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10326 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10330 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10333 GList *iter = fillins;
10335 cur_coord = (VikCoord *)(iter->data);
10336 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10337 rect = g_malloc(sizeof(Rect));
10340 rect->center = *cur_coord;
10341 rects_to_download = g_list_prepend(rects_to_download, rect);
10346 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10347 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10351 for (iter = fillins; iter; iter = iter->next)
10352 g_free(iter->data);
10353 g_list_free(fillins);
10355 if (rects_to_download) {
10356 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10357 g_free(rect_iter->data);
10358 g_list_free(rects_to_download);
10362 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10366 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10367 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10368 gint selected_zoom, default_zoom;
10370 VikTrwLayer *vtl = values[MA_VTL];
10371 VikLayersPanel *vlp = values[MA_VLP];
10373 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10374 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10376 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10380 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10382 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10383 int num_maps = g_list_length(vmls);
10386 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10390 // Convert from list of vmls to list of names. Allowing the user to select one of them
10391 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10392 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10394 gchar **np = map_names;
10395 VikMapsLayer **lp = map_layers;
10397 for (i = 0; i < num_maps; i++) {
10398 vml = (VikMapsLayer *)(vmls->data);
10400 *np++ = vik_maps_layer_get_map_label(vml);
10403 // Mark end of the array lists
10407 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10408 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10409 if (cur_zoom == zoom_vals[default_zoom])
10412 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10414 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10417 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10420 for (i = 0; i < num_maps; i++)
10421 g_free(map_names[i]);
10423 g_free(map_layers);
10429 /**** lowest waypoint number calculation ***/
10430 static gint highest_wp_number_name_to_number(const gchar *name) {
10431 if ( strlen(name) == 3 ) {
10432 int n = atoi(name);
10433 if ( n < 100 && name[0] != '0' )
10435 if ( n < 10 && name[0] != '0' )
10443 static void highest_wp_number_reset(VikTrwLayer *vtl)
10445 vtl->highest_wp_number = -1;
10448 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10450 /* if is bigger that top, add it */
10451 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10452 if ( new_wp_num > vtl->highest_wp_number )
10453 vtl->highest_wp_number = new_wp_num;
10456 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10458 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10459 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10460 if ( vtl->highest_wp_number == old_wp_num ) {
10462 vtl->highest_wp_number--;
10464 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10465 /* search down until we find something that *does* exist */
10467 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10468 vtl->highest_wp_number--;
10469 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10474 /* get lowest unused number */
10475 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10478 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10480 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10481 return g_strdup(buf);
10485 * trw_layer_create_track_list_both:
10487 * Create the latest list of tracks and routes
10489 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10491 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10492 GList *tracks = NULL;
10493 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10494 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10496 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10499 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10501 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10503 gchar *title = NULL;
10504 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10505 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10507 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10509 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10513 static void trw_layer_track_list_dialog ( menu_array_layer values )
10515 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10517 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10518 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10522 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10524 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10526 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10527 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );