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 );
3285 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3287 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3290 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3292 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3295 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3297 menu_array_layer data;
3298 data[MA_VTL] = values[MA_VTL];
3299 data[MA_VLP] = values[MA_VLP];
3301 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3303 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3304 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3306 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3308 if ( !trk || !trk->name )
3311 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3313 gchar *label = NULL;
3314 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3315 label = _("Export Route as GPX");
3317 label = _("Export Track as GPX");
3318 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3320 g_free ( auto_save_name );
3323 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3325 wpu_udata *user_data = udata;
3326 if ( wp == user_data->wp ) {
3327 user_data->uuid = id;
3333 static void trw_layer_goto_wp ( menu_array_layer values )
3335 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3336 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3337 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3338 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3339 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3341 GTK_RESPONSE_REJECT,
3343 GTK_RESPONSE_ACCEPT,
3346 GtkWidget *label, *entry;
3347 label = gtk_label_new(_("Waypoint Name:"));
3348 entry = gtk_entry_new();
3350 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3351 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3352 gtk_widget_show_all ( label );
3353 gtk_widget_show_all ( entry );
3355 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3357 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3359 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3360 // Find *first* wp with the given name
3361 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3364 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3367 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3368 vik_layers_panel_emit_update ( vlp );
3370 // Find and select on the side panel
3375 // Hmmm, want key of it
3376 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3378 if ( wpf && udata.uuid ) {
3379 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3380 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3389 gtk_widget_destroy ( dia );
3392 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3394 gchar *default_name = highest_wp_number_get(vtl);
3395 VikWaypoint *wp = vik_waypoint_new();
3396 gchar *returned_name;
3398 wp->coord = *def_coord;
3400 // Attempt to auto set height if DEM data is available
3401 vik_waypoint_apply_dem_data ( wp, TRUE );
3403 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3405 if ( returned_name )
3408 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3409 g_free (default_name);
3410 g_free (returned_name);
3413 g_free (default_name);
3414 vik_waypoint_free(wp);
3418 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3420 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3421 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3422 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3423 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3424 VikViewport *vvp = vik_window_viewport(vw);
3426 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3427 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3428 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3429 trw_layer_calculate_bounds_waypoints ( vtl );
3430 vik_layers_panel_emit_update ( vlp );
3433 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3435 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3436 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3437 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3439 trw_layer_find_maxmin (vtl, maxmin);
3440 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3441 trw_layer_calculate_bounds_waypoints ( vtl );
3442 vik_layers_panel_emit_update ( vlp );
3445 #ifdef VIK_CONFIG_GEOTAG
3446 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3448 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3450 // Update directly - not changing the mtime
3451 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3454 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3456 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3459 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3463 * Use code in separate file for this feature as reasonably complex
3465 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3467 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3468 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3469 // Unset so can be reverified later if necessary
3470 vtl->has_verified_thumbnails = FALSE;
3472 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3478 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3480 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3481 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3483 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3489 static void trw_layer_geotagging ( menu_array_layer values )
3491 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3492 // Unset so can be reverified later if necessary
3493 vtl->has_verified_thumbnails = FALSE;
3495 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3502 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3504 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3506 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3507 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3508 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3509 VikViewport *vvp = vik_window_viewport(vw);
3511 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3515 * Acquire into this TRW Layer straight from GPS Device
3517 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3519 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3520 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3524 * Acquire into this TRW Layer from Directions
3526 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3528 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3532 * Acquire into this TRW Layer from an entered URL
3534 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3536 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3537 trw_layer_acquire ( values, &vik_datasource_url_interface );
3540 #ifdef VIK_CONFIG_OPENSTREETMAP
3542 * Acquire into this TRW Layer from OSM
3544 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3546 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3550 * Acquire into this TRW Layer from OSM for 'My' Traces
3552 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3554 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3558 #ifdef VIK_CONFIG_GEOCACHES
3560 * Acquire into this TRW Layer from Geocaching.com
3562 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3564 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3568 #ifdef VIK_CONFIG_GEOTAG
3570 * Acquire into this TRW Layer from images
3572 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3574 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3576 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3577 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3579 // Reverify thumbnails as they may have changed
3580 vtl->has_verified_thumbnails = FALSE;
3581 trw_layer_verify_thumbnails ( vtl, NULL );
3585 static void trw_layer_gps_upload ( menu_array_layer values )
3587 menu_array_sublayer data;
3589 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3591 data[MA_VTL] = values[MA_VTL];
3592 data[MA_VLP] = values[MA_VLP];
3594 trw_layer_gps_upload_any ( data );
3598 * If pass_along[3] is defined that this will upload just that track
3600 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3602 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3603 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3605 // May not actually get a track here as values[2&3] can be null
3606 VikTrack *track = NULL;
3607 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3608 gboolean xfer_all = FALSE;
3610 if ( values[MA_SUBTYPE] ) {
3612 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3613 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3616 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3617 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3620 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3623 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3627 else if ( !values[MA_CONFIRM] )
3628 xfer_all = TRUE; // i.e. whole layer
3630 if (track && !track->visible) {
3631 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3635 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3636 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3637 GTK_DIALOG_DESTROY_WITH_PARENT,
3639 GTK_RESPONSE_ACCEPT,
3641 GTK_RESPONSE_REJECT,
3644 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3645 GtkWidget *response_w = NULL;
3646 #if GTK_CHECK_VERSION (2, 20, 0)
3647 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3651 gtk_widget_grab_focus ( response_w );
3653 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3655 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3656 datasource_gps_clean_up ( dgs );
3657 gtk_widget_destroy ( dialog );
3661 // Get info from reused datasource dialog widgets
3662 gchar* protocol = datasource_gps_get_protocol ( dgs );
3663 gchar* port = datasource_gps_get_descriptor ( dgs );
3664 // NB don't free the above strings as they're references to values held elsewhere
3665 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3666 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3667 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3668 gboolean turn_off = datasource_gps_get_off ( dgs );
3670 gtk_widget_destroy ( dialog );
3672 // When called from the viewport - work the corresponding layerspanel:
3674 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3677 // Apply settings to transfer to the GPS device
3684 vik_layers_panel_get_viewport (vlp),
3693 * Acquire into this TRW Layer from any GPS Babel supported file
3695 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3697 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3698 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3699 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3700 VikViewport *vvp = vik_window_viewport(vw);
3702 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3705 static void trw_layer_new_wp ( menu_array_layer values )
3707 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3708 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3709 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3710 instead return true if you want to update. */
3711 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 ) {
3712 trw_layer_calculate_bounds_waypoints ( vtl );
3713 vik_layers_panel_emit_update ( vlp );
3717 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3719 vtl->current_track = vik_track_new();
3720 vik_track_set_defaults ( vtl->current_track );
3721 vtl->current_track->visible = TRUE;
3722 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3723 // Create track with the preferred colour from the layer properties
3724 vtl->current_track->color = vtl->track_color;
3726 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3727 vtl->current_track->has_color = TRUE;
3728 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3731 static void trw_layer_new_track ( menu_array_layer values )
3733 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3735 if ( ! vtl->current_track ) {
3736 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3737 new_track_create_common ( vtl, name );
3740 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3744 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3746 vtl->current_track = vik_track_new();
3747 vik_track_set_defaults ( vtl->current_track );
3748 vtl->current_track->visible = TRUE;
3749 vtl->current_track->is_route = TRUE;
3750 // By default make all routes red
3751 vtl->current_track->has_color = TRUE;
3752 gdk_color_parse ( "red", &vtl->current_track->color );
3753 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3756 static void trw_layer_new_route ( menu_array_layer values )
3758 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3760 if ( ! vtl->current_track ) {
3761 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3762 new_route_create_common ( vtl, name );
3764 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3768 static void trw_layer_auto_routes_view ( menu_array_layer values )
3770 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3771 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3773 if ( g_hash_table_size (vtl->routes) > 0 ) {
3774 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3775 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3776 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3777 vik_layers_panel_emit_update ( vlp );
3782 static void trw_layer_finish_track ( menu_array_layer values )
3784 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3785 vtl->current_track = NULL;
3786 vik_layer_emit_update ( VIK_LAYER(vtl) );
3789 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3791 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3792 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3794 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3795 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3796 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3797 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3798 vik_layers_panel_emit_update ( vlp );
3802 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3804 /* NB do not care if wp is visible or not */
3805 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3808 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3810 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3811 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3813 /* Only 1 waypoint - jump straight to it */
3814 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3815 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3816 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3818 /* If at least 2 waypoints - find center and then zoom to fit */
3819 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3821 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3822 maxmin[0].lat = vtl->waypoints_bbox.north;
3823 maxmin[1].lat = vtl->waypoints_bbox.south;
3824 maxmin[0].lon = vtl->waypoints_bbox.east;
3825 maxmin[1].lon = vtl->waypoints_bbox.west;
3826 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3829 vik_layers_panel_emit_update ( vlp );
3832 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3834 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3837 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3839 if ( values[MA_MISC] ) {
3840 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3841 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3845 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3847 static menu_array_layer pass_along;
3849 GtkWidget *export_submenu;
3850 pass_along[MA_VTL] = vtl;
3851 pass_along[MA_VLP] = vlp;
3853 item = gtk_menu_item_new();
3854 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3855 gtk_widget_show ( item );
3857 if ( vtl->current_track ) {
3858 if ( vtl->current_track->is_route )
3859 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3861 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3863 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3864 gtk_widget_show ( item );
3867 item = gtk_menu_item_new ();
3868 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3869 gtk_widget_show ( item );
3872 /* Now with icons */
3873 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3874 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3875 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3876 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3877 gtk_widget_show ( item );
3879 GtkWidget *view_submenu = gtk_menu_new();
3880 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3882 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3883 gtk_widget_show ( item );
3884 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3886 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3888 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3889 gtk_widget_show ( item );
3891 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_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 _Waypoints") );
3897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3898 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3899 gtk_widget_show ( item );
3901 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3902 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3903 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3904 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3905 gtk_widget_show ( item );
3907 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3909 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3910 gtk_widget_show ( item );
3912 export_submenu = gtk_menu_new ();
3913 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3915 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3916 gtk_widget_show ( item );
3917 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3919 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3921 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3922 gtk_widget_show ( item );
3924 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3925 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), 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 _GPX...") );
3930 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), 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 _KML...") );
3935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3936 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3937 gtk_widget_show ( item );
3939 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3940 item = gtk_menu_item_new_with_mnemonic ( external1 );
3941 g_free ( external1 );
3942 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3943 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3944 gtk_widget_show ( item );
3946 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
3947 item = gtk_menu_item_new_with_mnemonic ( external2 );
3948 g_free ( external2 );
3949 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3950 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3951 gtk_widget_show ( item );
3953 GtkWidget *new_submenu = gtk_menu_new();
3954 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3955 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3956 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3957 gtk_widget_show(item);
3958 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3960 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3961 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3963 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3964 gtk_widget_show ( item );
3966 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3967 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3969 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3970 gtk_widget_show ( item );
3971 // Make it available only when a new track *not* already in progress
3972 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3974 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3977 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3978 gtk_widget_show ( item );
3979 // Make it available only when a new track *not* already in progress
3980 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3982 #ifdef VIK_CONFIG_GEOTAG
3983 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3984 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3985 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3986 gtk_widget_show ( item );
3989 GtkWidget *acquire_submenu = gtk_menu_new ();
3990 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3991 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3992 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3993 gtk_widget_show ( item );
3994 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3996 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3998 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3999 gtk_widget_show ( item );
4001 /* FIXME: only add menu when at least a routing engine has support for Directions */
4002 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4004 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4005 gtk_widget_show ( item );
4007 #ifdef VIK_CONFIG_OPENSTREETMAP
4008 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4010 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4011 gtk_widget_show ( item );
4013 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4014 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4015 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4016 gtk_widget_show ( item );
4019 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4021 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4022 gtk_widget_show ( item );
4024 #ifdef VIK_CONFIG_GEONAMES
4025 GtkWidget *wikipedia_submenu = gtk_menu_new();
4026 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4028 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4029 gtk_widget_show(item);
4030 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4032 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4035 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4036 gtk_widget_show ( item );
4038 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4041 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4042 gtk_widget_show ( item );
4045 #ifdef VIK_CONFIG_GEOCACHES
4046 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4048 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4049 gtk_widget_show ( item );
4052 #ifdef VIK_CONFIG_GEOTAG
4053 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4055 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4056 gtk_widget_show ( item );
4059 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4061 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4062 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4063 gtk_widget_show ( item );
4065 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4067 GtkWidget *upload_submenu = gtk_menu_new ();
4068 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4070 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4071 gtk_widget_show ( item );
4072 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4074 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4077 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4078 gtk_widget_show ( item );
4080 #ifdef VIK_CONFIG_OPENSTREETMAP
4081 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4082 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4084 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4085 gtk_widget_show ( item );
4088 GtkWidget *delete_submenu = gtk_menu_new ();
4089 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4090 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4092 gtk_widget_show ( item );
4093 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4095 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4096 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4097 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4098 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4099 gtk_widget_show ( item );
4101 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4104 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4105 gtk_widget_show ( item );
4107 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4108 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4110 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4111 gtk_widget_show ( item );
4113 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4116 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4117 gtk_widget_show ( item );
4119 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4120 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4122 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4123 gtk_widget_show ( item );
4125 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4126 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4128 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4129 gtk_widget_show ( item );
4131 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4132 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4134 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4135 gtk_widget_show ( item );
4138 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4139 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4141 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4142 gtk_widget_show ( item );
4145 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4146 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4148 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4149 gtk_widget_show ( item );
4150 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4152 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4153 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4155 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4156 gtk_widget_show ( item );
4157 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4160 // Fake Waypoint UUIDs vi simple increasing integer
4161 static guint wp_uuid = 0;
4163 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4167 vik_waypoint_set_name (wp, name);
4169 if ( VIK_LAYER(vtl)->realized )
4171 // Do we need to create the sublayer:
4172 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4173 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4176 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4178 // Visibility column always needed for waypoints
4179 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 );
4181 // Actual setting of visibility dependent on the waypoint
4182 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4184 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4186 // Sort now as post_read is not called on a realized waypoint
4187 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4190 highest_wp_number_add_wp(vtl, name);
4191 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4195 // Fake Track UUIDs vi simple increasing integer
4196 static guint tr_uuid = 0;
4198 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4202 vik_track_set_name (t, name);
4204 if ( VIK_LAYER(vtl)->realized )
4206 // Do we need to create the sublayer:
4207 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4208 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4211 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4212 // Visibility column always needed for tracks
4213 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 );
4215 // Actual setting of visibility dependent on the track
4216 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4218 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4220 // Sort now as post_read is not called on a realized track
4221 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4224 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4226 trw_layer_update_treeview ( vtl, t );
4229 // Fake Route UUIDs vi simple increasing integer
4230 static guint rt_uuid = 0;
4232 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4236 vik_track_set_name (t, name);
4238 if ( VIK_LAYER(vtl)->realized )
4240 // Do we need to create the sublayer:
4241 if ( g_hash_table_size (vtl->routes) == 0 ) {
4242 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4245 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4246 // Visibility column always needed for routes
4247 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 );
4248 // Actual setting of visibility dependent on the route
4249 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4251 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4253 // Sort now as post_read is not called on a realized route
4254 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4257 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4259 trw_layer_update_treeview ( vtl, t );
4262 /* to be called whenever a track has been deleted or may have been changed. */
4263 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4265 if (vtl->current_tp_track == trk )
4266 trw_layer_cancel_current_tp ( vtl, FALSE );
4270 * Normally this is done to due the waypoint size preference having changed
4272 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4274 GHashTableIter iter;
4275 gpointer key, value;
4278 g_hash_table_iter_init ( &iter, vtl->waypoints );
4279 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4280 VikWaypoint *wp = VIK_WAYPOINT(value);
4282 // Reapply symbol setting to update the pixbuf
4283 gchar *tmp_symbol = g_strdup ( wp->symbol );
4284 vik_waypoint_set_symbol ( wp, tmp_symbol );
4285 g_free ( tmp_symbol );
4291 * trw_layer_new_unique_sublayer_name:
4293 * Allocates a unique new name
4295 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4298 gchar *newname = g_strdup(name);
4303 switch ( sublayer_type ) {
4304 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4305 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4307 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4308 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4311 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4314 // If found a name already in use try adding 1 to it and we try again
4316 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4318 newname = new_newname;
4321 } while ( id != NULL);
4326 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4328 // No more uniqueness of name forced when loading from a file
4329 // This now makes this function a little redunant as we just flow the parameters through
4330 vik_trw_layer_add_waypoint ( vtl, name, wp );
4333 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4335 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4336 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4337 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4338 vik_track_free ( tr );
4339 vtl->route_finder_append = FALSE; /* this means we have added it */
4342 // No more uniqueness of name forced when loading from a file
4344 vik_trw_layer_add_route ( vtl, name, tr );
4346 vik_trw_layer_add_track ( vtl, name, tr );
4348 if ( vtl->route_finder_check_added_track ) {
4349 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4350 vtl->route_finder_added_track = tr;
4355 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4357 *l = g_list_append(*l, id);
4361 * Move an item from one TRW layer to another TRW layer
4363 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4365 // TODO reconsider strategy when moving within layer (if anything...)
4366 gboolean rename = ( vtl_src != vtl_dest );
4370 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4371 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4375 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4377 newname = g_strdup ( trk->name );
4379 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4380 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4382 vik_trw_layer_delete_track ( vtl_src, trk );
4385 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4386 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4390 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4392 newname = g_strdup ( trk->name );
4394 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4395 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4397 vik_trw_layer_delete_route ( vtl_src, trk );
4400 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4401 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4405 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4407 newname = g_strdup ( wp->name );
4409 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4410 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4412 trw_layer_delete_waypoint ( vtl_src, wp );
4414 // Recalculate bounds even if not renamed as maybe dragged between layers
4415 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4416 trw_layer_calculate_bounds_waypoints ( vtl_src );
4420 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4422 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4423 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4425 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4426 GList *items = NULL;
4429 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4430 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4432 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4433 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4435 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4436 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4441 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4442 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4443 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4444 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4446 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4453 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4454 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4458 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4460 trku_udata *user_data = udata;
4461 if ( trk == user_data->trk ) {
4462 user_data->uuid = id;
4468 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4470 gboolean was_visible = FALSE;
4472 if ( trk && trk->name ) {
4474 if ( trk == vtl->current_track ) {
4475 vtl->current_track = NULL;
4476 vtl->current_tp_track = NULL;
4477 vtl->current_tp_id = NULL;
4478 vtl->moving_tp = FALSE;
4481 was_visible = trk->visible;
4483 if ( trk == vtl->route_finder_current_track )
4484 vtl->route_finder_current_track = NULL;
4486 if ( trk == vtl->route_finder_added_track )
4487 vtl->route_finder_added_track = NULL;
4493 // Hmmm, want key of it
4494 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4496 if ( trkf && udata.uuid ) {
4497 /* could be current_tp, so we have to check */
4498 trw_layer_cancel_tps_of_track ( vtl, trk );
4500 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4503 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4504 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4505 g_hash_table_remove ( vtl->tracks, udata.uuid );
4507 // If last sublayer, then remove sublayer container
4508 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4509 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4517 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4519 gboolean was_visible = FALSE;
4521 if ( trk && trk->name ) {
4523 if ( trk == vtl->current_track ) {
4524 vtl->current_track = NULL;
4525 vtl->current_tp_track = NULL;
4526 vtl->current_tp_id = NULL;
4527 vtl->moving_tp = FALSE;
4530 was_visible = trk->visible;
4532 if ( trk == vtl->route_finder_current_track )
4533 vtl->route_finder_current_track = NULL;
4535 if ( trk == vtl->route_finder_added_track )
4536 vtl->route_finder_added_track = NULL;
4542 // Hmmm, want key of it
4543 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4545 if ( trkf && udata.uuid ) {
4546 /* could be current_tp, so we have to check */
4547 trw_layer_cancel_tps_of_track ( vtl, trk );
4549 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4552 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4553 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4554 g_hash_table_remove ( vtl->routes, udata.uuid );
4556 // If last sublayer, then remove sublayer container
4557 if ( g_hash_table_size (vtl->routes) == 0 ) {
4558 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4566 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4568 gboolean was_visible = FALSE;
4570 if ( wp && wp->name ) {
4572 if ( wp == vtl->current_wp ) {
4573 vtl->current_wp = NULL;
4574 vtl->current_wp_id = NULL;
4575 vtl->moving_wp = FALSE;
4578 was_visible = wp->visible;
4584 // Hmmm, want key of it
4585 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4587 if ( wpf && udata.uuid ) {
4588 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4591 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4592 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4594 highest_wp_number_remove_wp(vtl, wp->name);
4595 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4597 // If last sublayer, then remove sublayer container
4598 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4599 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4609 // Only for temporary use by trw_layer_delete_waypoint_by_name
4610 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4612 wpu_udata *user_data = udata;
4613 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4614 user_data->uuid = id;
4621 * Delete a waypoint by the given name
4622 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4623 * as there be multiple waypoints with the same name
4625 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4628 // Fake a waypoint with the given name
4629 udata.wp = vik_waypoint_new ();
4630 vik_waypoint_set_name (udata.wp, name);
4631 // Currently only the name is used in this waypoint find function
4634 // Hmmm, want key of it
4635 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4637 vik_waypoint_free (udata.wp);
4639 if ( wpf && udata.uuid )
4640 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4646 VikTrack *trk; // input
4647 gpointer uuid; // output
4650 // Only for temporary use by trw_layer_delete_track_by_name
4651 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4653 tpu_udata *user_data = udata;
4654 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4655 user_data->uuid = id;
4662 * Delete a track by the given name
4663 * NOTE: ATM this will delete the first encountered Track with the specified name
4664 * as there may be multiple tracks with the same name within the specified hash table
4666 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4669 // Fake a track with the given name
4670 udata.trk = vik_track_new ();
4671 vik_track_set_name (udata.trk, name);
4672 // Currently only the name is used in this waypoint find function
4675 // Hmmm, want key of it
4676 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4678 vik_track_free (udata.trk);
4680 if ( trkf && udata.uuid ) {
4681 // This could be a little better written...
4682 if ( vtl->tracks == ht_tracks )
4683 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4684 if ( vtl->routes == ht_tracks )
4685 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4692 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4694 vik_treeview_item_delete (vt, it );
4697 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4700 vtl->current_track = NULL;
4701 vtl->route_finder_current_track = NULL;
4702 vtl->route_finder_added_track = NULL;
4703 if (vtl->current_tp_track)
4704 trw_layer_cancel_current_tp(vtl, FALSE);
4706 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4707 g_hash_table_remove_all(vtl->routes_iters);
4708 g_hash_table_remove_all(vtl->routes);
4710 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4712 vik_layer_emit_update ( VIK_LAYER(vtl) );
4715 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4718 vtl->current_track = NULL;
4719 vtl->route_finder_current_track = NULL;
4720 vtl->route_finder_added_track = NULL;
4721 if (vtl->current_tp_track)
4722 trw_layer_cancel_current_tp(vtl, FALSE);
4724 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4725 g_hash_table_remove_all(vtl->tracks_iters);
4726 g_hash_table_remove_all(vtl->tracks);
4728 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4730 vik_layer_emit_update ( VIK_LAYER(vtl) );
4733 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4735 vtl->current_wp = NULL;
4736 vtl->current_wp_id = NULL;
4737 vtl->moving_wp = FALSE;
4739 highest_wp_number_reset(vtl);
4741 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4742 g_hash_table_remove_all(vtl->waypoints_iters);
4743 g_hash_table_remove_all(vtl->waypoints);
4745 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4747 vik_layer_emit_update ( VIK_LAYER(vtl) );
4750 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4752 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4753 // Get confirmation from the user
4754 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4755 _("Are you sure you want to delete all tracks in %s?"),
4756 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4757 vik_trw_layer_delete_all_tracks (vtl);
4760 static void trw_layer_delete_all_routes ( 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 routes in %s?"),
4766 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4767 vik_trw_layer_delete_all_routes (vtl);
4770 static void trw_layer_delete_all_waypoints ( 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 waypoints in %s?"),
4776 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4777 vik_trw_layer_delete_all_waypoints (vtl);
4780 static void trw_layer_delete_item ( menu_array_sublayer values )
4782 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4783 gboolean was_visible = FALSE;
4784 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4786 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4787 if ( wp && wp->name ) {
4788 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4789 // Get confirmation from the user
4790 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4791 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4792 _("Are you sure you want to delete the waypoint \"%s\"?"),
4795 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4798 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4800 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4801 if ( trk && trk->name ) {
4802 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4803 // Get confirmation from the user
4804 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4805 _("Are you sure you want to delete the track \"%s\"?"),
4808 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4813 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4814 if ( trk && trk->name ) {
4815 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4816 // Get confirmation from the user
4817 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4818 _("Are you sure you want to delete the route \"%s\"?"),
4821 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4825 vik_layer_emit_update ( VIK_LAYER(vtl) );
4829 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4831 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4833 vik_waypoint_set_name ( wp, new_name );
4835 // Now update the treeview as well
4840 // Need key of it for treeview update
4841 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4843 if ( wpf && udataU.uuid ) {
4844 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4847 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4848 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4854 * Maintain icon of waypoint in the treeview
4856 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4858 // update the treeview
4863 // Need key of it for treeview update
4864 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4866 if ( wpf && udataU.uuid ) {
4867 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4870 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4875 static void trw_layer_properties_item ( menu_array_sublayer values )
4877 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4878 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4880 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4882 if ( wp && wp->name )
4884 gboolean updated = FALSE;
4885 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4887 trw_layer_waypoint_rename ( vtl, wp, new_name );
4889 if ( updated && values[MA_TV_ITER] )
4890 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4892 if ( updated && VIK_LAYER(vtl)->visible )
4893 vik_layer_emit_update ( VIK_LAYER(vtl) );
4899 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4900 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4902 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4904 if ( tr && tr->name )
4906 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4917 * trw_layer_track_statistics:
4919 * Show track statistics.
4920 * ATM jump to the stats page in the properties
4921 * TODO: consider separating the stats into an individual dialog?
4923 static void trw_layer_track_statistics ( menu_array_sublayer values )
4925 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4927 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4928 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4930 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4932 if ( trk && trk->name ) {
4933 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4943 * Update the treeview of the track id - primarily to update the icon
4945 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4951 gpointer *trkf = NULL;
4952 if ( trk->is_route )
4953 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4955 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4957 if ( trkf && udata.uuid ) {
4959 GtkTreeIter *iter = NULL;
4960 if ( trk->is_route )
4961 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4963 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4966 // TODO: Make this a function
4967 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4968 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4969 ((trk->color.green & 0xff00) << 8) |
4970 (trk->color.blue & 0xff00);
4971 gdk_pixbuf_fill ( pixbuf, pixel );
4972 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4973 g_object_unref (pixbuf);
4980 Parameter 1 -> VikLayersPanel
4981 Parameter 2 -> VikLayer
4982 Parameter 3 -> VikViewport
4984 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4987 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4988 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4991 /* since vlp not set, vl & vvp should be valid instead! */
4993 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4994 vik_layer_emit_update ( VIK_LAYER(vl) );
4999 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5001 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5003 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5004 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5006 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5008 if ( track && track->trackpoints )
5009 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5012 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5014 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5016 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5017 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5019 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5021 if ( track && track->trackpoints )
5023 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5025 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5026 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5027 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5028 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5029 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5033 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5035 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5037 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5038 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5040 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5045 // Converting a track to a route can be a bit more complicated,
5046 // so give a chance to change our minds:
5047 if ( !trk->is_route &&
5048 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5049 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5051 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5052 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5057 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5060 trk_copy->is_route = !trk_copy->is_route;
5062 // ATM can't set name to self - so must create temporary copy
5063 gchar *name = g_strdup ( trk_copy->name );
5065 // Delete old one and then add new one
5066 if ( trk->is_route ) {
5067 vik_trw_layer_delete_route ( vtl, trk );
5068 vik_trw_layer_add_track ( vtl, name, trk_copy );
5071 // Extra route conversion bits...
5072 vik_track_merge_segments ( trk_copy );
5073 vik_track_to_routepoints ( trk_copy );
5075 vik_trw_layer_delete_track ( vtl, trk );
5076 vik_trw_layer_add_route ( vtl, name, trk_copy );
5080 // Update in case color of track / route changes when moving between sublayers
5081 vik_layer_emit_update ( VIK_LAYER(vtl) );
5084 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5086 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5088 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5089 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5091 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5094 vik_track_anonymize_times ( track );
5097 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5099 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5101 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5102 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5104 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5109 vtl->current_track = track;
5110 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);
5112 if ( track->trackpoints )
5113 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5117 * extend a track using route finder
5119 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5121 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5122 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5125 if ( !track->trackpoints )
5128 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5129 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5130 vtl->route_finder_current_track = track;
5131 vtl->route_finder_started = TRUE;
5133 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5139 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5141 // If have a vlp then perform a basic test to see if any DEM info available...
5143 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5145 if ( !g_list_length(dems) ) {
5146 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5154 * apply_dem_data_common:
5156 * A common function for applying the DEM values and reporting the results.
5158 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5160 if ( !trw_layer_dem_test ( vtl, vlp ) )
5163 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5164 // Inform user how much was changed
5166 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5167 g_snprintf(str, 64, tmp_str, changed);
5168 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5171 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5173 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5175 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5176 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5178 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5181 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5184 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5186 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5188 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5189 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5191 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5194 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5200 * A common function for applying the elevation smoothing and reporting the results.
5202 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5204 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5205 // Inform user how much was changed
5207 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5208 g_snprintf(str, 64, tmp_str, changed);
5209 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5215 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5217 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5219 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5220 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5222 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5227 smooth_it ( vtl, track, FALSE );
5230 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5232 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5234 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5235 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5237 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5242 smooth_it ( vtl, track, TRUE );
5246 * Commonal helper function
5248 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5251 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5252 g_snprintf(str, 64, tmp_str, changed);
5253 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5256 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5258 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5259 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5261 if ( !trw_layer_dem_test ( vtl, vlp ) )
5265 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5267 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5269 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5273 GHashTableIter iter;
5274 gpointer key, value;
5276 g_hash_table_iter_init ( &iter, vtl->waypoints );
5277 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5278 VikWaypoint *wp = VIK_WAYPOINT(value);
5279 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5282 wp_changed_message ( vtl, changed );
5285 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5287 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5288 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5290 if ( !trw_layer_dem_test ( vtl, vlp ) )
5294 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5296 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5298 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5302 GHashTableIter iter;
5303 gpointer key, value;
5305 g_hash_table_iter_init ( &iter, vtl->waypoints );
5306 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5307 VikWaypoint *wp = VIK_WAYPOINT(value);
5308 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5311 wp_changed_message ( vtl, changed );
5314 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5316 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5318 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5319 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5321 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5325 if ( !track->trackpoints )
5327 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5330 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5332 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5334 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5335 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5337 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5342 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5345 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5348 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5350 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5352 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5353 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5355 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5360 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5363 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5366 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5368 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5370 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5371 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5373 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5378 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5381 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5385 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5387 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5389 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5391 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5392 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5394 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5396 if ( trk && trk->trackpoints )
5398 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5399 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5400 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5401 if ( values[MA_VLP] )
5402 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5404 vik_layer_emit_update ( VIK_LAYER(vtl) );
5409 * Refine the selected track/route with a routing engine.
5410 * The routing engine is selected by the user, when requestiong the job.
5412 static void trw_layer_route_refine ( menu_array_sublayer values )
5414 static gint last_engine = 0;
5415 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5418 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5419 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5421 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5423 if ( trk && trk->trackpoints )
5425 /* Check size of the route */
5426 int nb = vik_track_get_tp_count(trk);
5428 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5429 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5430 GTK_MESSAGE_WARNING,
5431 GTK_BUTTONS_OK_CANCEL,
5432 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5434 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5435 gtk_widget_destroy ( dialog );
5436 if (response != GTK_RESPONSE_OK )
5439 /* Select engine from dialog */
5440 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5441 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5442 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5444 GTK_RESPONSE_REJECT,
5446 GTK_RESPONSE_ACCEPT,
5448 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5449 gtk_widget_show_all(label);
5451 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5453 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5454 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5455 gtk_widget_show_all(combo);
5457 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5459 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5461 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5463 /* Dialog validated: retrieve selected engine and do the job */
5464 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5465 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5468 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5470 /* Force saving track */
5471 /* FIXME: remove or rename this hack */
5472 vtl->route_finder_check_added_track = TRUE;
5475 vik_routing_engine_refine (routing, vtl, trk);
5477 /* FIXME: remove or rename this hack */
5478 if ( vtl->route_finder_added_track )
5479 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5481 vtl->route_finder_added_track = NULL;
5482 vtl->route_finder_check_added_track = FALSE;
5484 vik_layer_emit_update ( VIK_LAYER(vtl) );
5486 /* Restore cursor */
5487 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5489 gtk_widget_destroy ( dialog );
5493 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5495 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5496 trw_layer_tpwin_init ( vtl );
5499 /*************************************
5500 * merge/split by time routines
5501 *************************************/
5503 /* called for each key in track hash table.
5504 * If the current track has the same time stamp type, add it to the result,
5505 * except the one pointed by "exclude".
5506 * set exclude to NULL if there is no exclude to check.
5507 * Note that the result is in reverse (for performance reasons).
5512 gboolean with_timestamps;
5514 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5516 twt_udata *user_data = udata;
5517 VikTrackpoint *p1, *p2;
5518 VikTrack *trk = VIK_TRACK(value);
5519 if (trk == (VikTrack *)user_data->exclude) {
5523 if (trk->trackpoints) {
5524 p1 = vik_track_get_tp_first(trk);
5525 p2 = vik_track_get_tp_last(trk);
5527 if ( user_data->with_timestamps ) {
5528 if (!p1->has_timestamp || !p2->has_timestamp) {
5533 // Don't add tracks with timestamps when getting non timestamp tracks
5534 if (p1->has_timestamp || p2->has_timestamp) {
5540 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5543 /* called for each key in track hash table. if original track user_data[1] is close enough
5544 * to the passed one, add it to list in user_data[0]
5546 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5549 VikTrackpoint *p1, *p2;
5550 VikTrack *trk = VIK_TRACK(value);
5552 GList **nearby_tracks = ((gpointer *)user_data)[0];
5553 GList *tpoints = ((gpointer *)user_data)[1];
5556 * detect reasons for not merging, and return
5557 * if no reason is found not to merge, then do it.
5560 // Exclude the original track from the compiled list
5561 if (trk->trackpoints == tpoints) {
5565 t1 = vik_track_get_tp_first(trk)->timestamp;
5566 t2 = vik_track_get_tp_last(trk)->timestamp;
5568 if (trk->trackpoints) {
5569 p1 = vik_track_get_tp_first(trk);
5570 p2 = vik_track_get_tp_last(trk);
5572 if (!p1->has_timestamp || !p2->has_timestamp) {
5573 //g_print("no timestamp\n");
5577 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5578 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5579 if (! (abs(t1 - p2->timestamp) < threshold ||
5581 abs(p1->timestamp - t2) < threshold)
5588 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5591 /* comparison function used to sort tracks; a and b are hash table keys */
5592 /* Not actively used - can be restored if needed
5593 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5595 GHashTable *tracks = user_data;
5598 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5599 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5601 if (t1 < t2) return -1;
5602 if (t1 > t2) return 1;
5607 /* comparison function used to sort trackpoints */
5608 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5610 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5612 if (t1 < t2) return -1;
5613 if (t1 > t2) return 1;
5618 * comparison function which can be used to sort tracks or waypoints by name
5620 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5622 const gchar* namea = (const gchar*) a;
5623 const gchar* nameb = (const gchar*) b;
5624 if ( namea == NULL || nameb == NULL)
5627 // Same sort method as used in the vik_treeview_*_alphabetize functions
5628 return strcmp ( namea, nameb );
5632 * Attempt to merge selected track with other tracks specified by the user
5633 * Tracks to merge with must be of the same 'type' as the selected track -
5634 * either all with timestamps, or all without timestamps
5636 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5638 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5639 GList *other_tracks = NULL;
5640 GHashTable *ght_tracks;
5641 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5642 ght_tracks = vtl->routes;
5644 ght_tracks = vtl->tracks;
5646 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5651 if ( !track->trackpoints )
5655 udata.result = &other_tracks;
5656 udata.exclude = track->trackpoints;
5657 // Allow merging with 'similar' time type time tracks
5658 // i.e. either those times, or those without
5659 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5661 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5662 other_tracks = g_list_reverse(other_tracks);
5664 if ( !other_tracks ) {
5665 if ( udata.with_timestamps )
5666 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5668 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5672 // Sort alphabetically for user presentation
5673 // Convert into list of names for usage with dialog function
5674 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5675 GList *other_tracks_names = NULL;
5676 GList *iter = g_list_first ( other_tracks );
5678 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5679 iter = g_list_next ( iter );
5682 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5684 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5688 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5689 g_list_free(other_tracks);
5690 g_list_free(other_tracks_names);
5695 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5696 VikTrack *merge_track;
5697 if ( track->is_route )
5698 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5700 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5703 vik_track_steal_and_append_trackpoints ( track, merge_track );
5704 if ( track->is_route )
5705 vik_trw_layer_delete_route (vtl, merge_track);
5707 vik_trw_layer_delete_track (vtl, merge_track);
5708 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5711 for (l = merge_list; l != NULL; l = g_list_next(l))
5713 g_list_free(merge_list);
5715 vik_layer_emit_update( VIK_LAYER(vtl) );
5719 // c.f. trw_layer_sorted_track_id_by_name_list
5720 // but don't add the specified track to the list (normally current track)
5721 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5723 twt_udata *user_data = udata;
5726 if (trk->trackpoints == user_data->exclude) {
5730 // Sort named list alphabetically
5731 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5735 * Join - this allows combining 'tracks' and 'track routes'
5736 * i.e. doesn't care about whether tracks have consistent timestamps
5737 * ATM can only append one track at a time to the currently selected track
5739 static void trw_layer_append_track ( menu_array_sublayer values )
5742 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5744 GHashTable *ght_tracks;
5745 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5746 ght_tracks = vtl->routes;
5748 ght_tracks = vtl->tracks;
5750 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5755 GList *other_tracks_names = NULL;
5757 // Sort alphabetically for user presentation
5758 // Convert into list of names for usage with dialog function
5759 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5761 udata.result = &other_tracks_names;
5762 udata.exclude = trk->trackpoints;
5764 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5766 // Note the limit to selecting one track only
5767 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5768 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5769 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5772 trk->is_route ? _("Append Route"): _("Append Track"),
5773 trk->is_route ? _("Select the route to append after the current route") :
5774 _("Select the track to append after the current track") );
5776 g_list_free(other_tracks_names);
5778 // It's a list, but shouldn't contain more than one other track!
5779 if ( append_list ) {
5781 for (l = append_list; l != NULL; l = g_list_next(l)) {
5782 // TODO: at present this uses the first track found by name,
5783 // which with potential multiple same named tracks may not be the one selected...
5784 VikTrack *append_track;
5785 if ( trk->is_route )
5786 append_track = vik_trw_layer_get_route ( vtl, l->data );
5788 append_track = vik_trw_layer_get_track ( vtl, l->data );
5790 if ( append_track ) {
5791 vik_track_steal_and_append_trackpoints ( trk, append_track );
5792 if ( trk->is_route )
5793 vik_trw_layer_delete_route (vtl, append_track);
5795 vik_trw_layer_delete_track (vtl, append_track);
5798 for (l = append_list; l != NULL; l = g_list_next(l))
5800 g_list_free(append_list);
5802 vik_layer_emit_update( VIK_LAYER(vtl) );
5807 * Very similar to trw_layer_append_track for joining
5808 * but this allows selection from the 'other' list
5809 * If a track is selected, then is shows routes and joins the selected one
5810 * If a route is selected, then is shows tracks and joins the selected one
5812 static void trw_layer_append_other ( menu_array_sublayer values )
5815 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5817 GHashTable *ght_mykind, *ght_others;
5818 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5819 ght_mykind = vtl->routes;
5820 ght_others = vtl->tracks;
5823 ght_mykind = vtl->tracks;
5824 ght_others = vtl->routes;
5827 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5832 GList *other_tracks_names = NULL;
5834 // Sort alphabetically for user presentation
5835 // Convert into list of names for usage with dialog function
5836 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5838 udata.result = &other_tracks_names;
5839 udata.exclude = trk->trackpoints;
5841 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5843 // Note the limit to selecting one track only
5844 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5845 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5846 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5849 trk->is_route ? _("Append Track"): _("Append Route"),
5850 trk->is_route ? _("Select the track to append after the current route") :
5851 _("Select the route to append after the current track") );
5853 g_list_free(other_tracks_names);
5855 // It's a list, but shouldn't contain more than one other track!
5856 if ( append_list ) {
5858 for (l = append_list; l != NULL; l = g_list_next(l)) {
5859 // TODO: at present this uses the first track found by name,
5860 // which with potential multiple same named tracks may not be the one selected...
5862 // Get FROM THE OTHER TYPE list
5863 VikTrack *append_track;
5864 if ( trk->is_route )
5865 append_track = vik_trw_layer_get_track ( vtl, l->data );
5867 append_track = vik_trw_layer_get_route ( vtl, l->data );
5869 if ( append_track ) {
5871 if ( !append_track->is_route &&
5872 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5873 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5875 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5876 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5877 vik_track_merge_segments ( append_track );
5878 vik_track_to_routepoints ( append_track );
5885 vik_track_steal_and_append_trackpoints ( trk, append_track );
5887 // Delete copied which is FROM THE OTHER TYPE list
5888 if ( trk->is_route )
5889 vik_trw_layer_delete_track (vtl, append_track);
5891 vik_trw_layer_delete_route (vtl, append_track);
5894 for (l = append_list; l != NULL; l = g_list_next(l))
5896 g_list_free(append_list);
5897 vik_layer_emit_update( VIK_LAYER(vtl) );
5901 /* merge by segments */
5902 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5904 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5905 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5906 guint segments = vik_track_merge_segments ( trk );
5907 // NB currently no need to redraw as segments not actually shown on the display
5908 // However inform the user of what happened:
5910 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5911 g_snprintf(str, 64, tmp_str, segments);
5912 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5915 /* merge by time routine */
5916 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5918 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5922 GList *tracks_with_timestamp = NULL;
5923 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5924 if (orig_trk->trackpoints &&
5925 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
5926 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5931 udata.result = &tracks_with_timestamp;
5932 udata.exclude = orig_trk->trackpoints;
5933 udata.with_timestamps = TRUE;
5934 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5935 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5937 if (!tracks_with_timestamp) {
5938 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5941 g_list_free(tracks_with_timestamp);
5943 static guint threshold_in_minutes = 1;
5944 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5945 _("Merge Threshold..."),
5946 _("Merge when time between tracks less than:"),
5947 &threshold_in_minutes)) {
5951 // keep attempting to merge all tracks until no merges within the time specified is possible
5952 gboolean attempt_merge = TRUE;
5953 GList *nearby_tracks = NULL;
5955 static gpointer params[3];
5957 while ( attempt_merge ) {
5959 // Don't try again unless tracks have changed
5960 attempt_merge = FALSE;
5962 trps = orig_trk->trackpoints;
5966 if (nearby_tracks) {
5967 g_list_free(nearby_tracks);
5968 nearby_tracks = NULL;
5971 params[0] = &nearby_tracks;
5972 params[1] = (gpointer)trps;
5973 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5975 /* get a list of adjacent-in-time tracks */
5976 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5979 GList *l = nearby_tracks;
5981 /* remove trackpoints from merged track, delete track */
5982 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5983 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5985 // Tracks have changed, therefore retry again against all the remaining tracks
5986 attempt_merge = TRUE;
5991 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5994 g_list_free(nearby_tracks);
5996 vik_layer_emit_update( VIK_LAYER(vtl) );
6000 * Split a track at the currently selected trackpoint
6002 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6004 if ( !vtl->current_tpl )
6007 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6008 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6010 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6011 GList *newglist = g_list_alloc ();
6012 newglist->prev = NULL;
6013 newglist->next = vtl->current_tpl->next;
6014 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6015 tr->trackpoints = newglist;
6017 vtl->current_tpl->next->prev = newglist; /* end old track here */
6018 vtl->current_tpl->next = NULL;
6020 // Bounds of the selected track changed due to the split
6021 vik_track_calculate_bounds ( vtl->current_tp_track );
6023 vtl->current_tpl = newglist; /* change tp to first of new track. */
6024 vtl->current_tp_track = tr;
6027 vik_trw_layer_add_route ( vtl, name, tr );
6029 vik_trw_layer_add_track ( vtl, name, tr );
6031 // Bounds of the new track created by the split
6032 vik_track_calculate_bounds ( tr );
6038 // Also need id of newly created track
6041 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6043 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6045 if ( trkf && udata.uuid )
6046 vtl->current_tp_id = udata.uuid;
6048 vtl->current_tp_id = NULL;
6050 vik_layer_emit_update(VIK_LAYER(vtl));
6056 /* split by time routine */
6057 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6059 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6060 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6061 GList *trps = track->trackpoints;
6063 GList *newlists = NULL;
6064 GList *newtps = NULL;
6065 static guint thr = 1;
6072 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6073 _("Split Threshold..."),
6074 _("Split when time between trackpoints exceeds:"),
6079 /* iterate through trackpoints, and copy them into new lists without touching original list */
6080 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6084 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6086 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6089 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6090 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6091 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6093 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6098 if (ts - prev_ts > thr*60) {
6099 /* flush accumulated trackpoints into new list */
6100 newlists = g_list_append(newlists, g_list_reverse(newtps));
6104 /* accumulate trackpoint copies in newtps, in reverse order */
6105 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6107 iter = g_list_next(iter);
6110 newlists = g_list_append(newlists, g_list_reverse(newtps));
6113 /* put lists of trackpoints into tracks */
6115 // Only bother updating if the split results in new tracks
6116 if (g_list_length (newlists) > 1) {
6121 tr = vik_track_copy ( track, FALSE );
6122 tr->trackpoints = (GList *)(iter->data);
6124 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6125 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6126 g_free ( new_tr_name );
6127 vik_track_calculate_bounds ( tr );
6128 iter = g_list_next(iter);
6130 // Remove original track and then update the display
6131 vik_trw_layer_delete_track (vtl, track);
6132 vik_layer_emit_update(VIK_LAYER(vtl));
6134 g_list_free(newlists);
6138 * Split a track by the number of points as specified by the user
6140 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6142 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6144 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6145 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6147 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6152 // Check valid track
6153 GList *trps = track->trackpoints;
6157 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6158 _("Split Every Nth Point"),
6159 _("Split on every Nth point:"),
6160 250, // Default value as per typical limited track capacity of various GPS devices
6164 // Was a valid number returned?
6170 GList *newlists = NULL;
6171 GList *newtps = NULL;
6176 /* accumulate trackpoint copies in newtps, in reverse order */
6177 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6179 if (count >= points) {
6180 /* flush accumulated trackpoints into new list */
6181 newlists = g_list_append(newlists, g_list_reverse(newtps));
6185 iter = g_list_next(iter);
6188 // If there is a remaining chunk put that into the new split list
6189 // This may well be the whole track if no split points were encountered
6191 newlists = g_list_append(newlists, g_list_reverse(newtps));
6194 /* put lists of trackpoints into tracks */
6196 // Only bother updating if the split results in new tracks
6197 if (g_list_length (newlists) > 1) {
6202 tr = vik_track_copy ( track, FALSE );
6203 tr->trackpoints = (GList *)(iter->data);
6205 if ( track->is_route ) {
6206 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6207 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6210 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6211 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6213 g_free ( new_tr_name );
6214 vik_track_calculate_bounds ( tr );
6216 iter = g_list_next(iter);
6218 // Remove original track and then update the display
6219 if ( track->is_route )
6220 vik_trw_layer_delete_route (vtl, track);
6222 vik_trw_layer_delete_track (vtl, track);
6223 vik_layer_emit_update(VIK_LAYER(vtl));
6225 g_list_free(newlists);
6229 * Split a track at the currently selected trackpoint
6231 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6233 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6234 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6235 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6239 * Split a track by its segments
6240 * Routes do not have segments so don't call this for routes
6242 static void trw_layer_split_segments ( menu_array_sublayer values )
6244 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6245 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6252 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6255 for ( i = 0; i < ntracks; i++ ) {
6257 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6258 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6259 g_free ( new_tr_name );
6264 // Remove original track
6265 vik_trw_layer_delete_track ( vtl, trk );
6266 vik_layer_emit_update ( VIK_LAYER(vtl) );
6269 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6272 /* end of split/merge routines */
6274 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6278 // Find available adjacent trackpoint
6279 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6280 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6281 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6283 // Delete current trackpoint
6284 vik_trackpoint_free ( vtl->current_tpl->data );
6285 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6287 // Set to current to the available adjacent trackpoint
6288 vtl->current_tpl = new_tpl;
6290 if ( vtl->current_tp_track ) {
6291 vik_track_calculate_bounds ( vtl->current_tp_track );
6295 // Delete current trackpoint
6296 vik_trackpoint_free ( vtl->current_tpl->data );
6297 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6298 trw_layer_cancel_current_tp ( vtl, FALSE );
6303 * Delete the selected point
6305 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6307 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6309 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6310 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6312 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6317 if ( !vtl->current_tpl )
6320 trw_layer_trackpoint_selected_delete ( vtl, trk );
6322 // Track has been updated so update tps:
6323 trw_layer_cancel_tps_of_track ( vtl, trk );
6325 vik_layer_emit_update ( VIK_LAYER(vtl) );
6329 * Delete adjacent track points at the same position
6330 * AKA Delete Dulplicates on the Properties Window
6332 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6334 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6336 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6337 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6339 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6344 gulong removed = vik_track_remove_dup_points ( trk );
6346 // Track has been updated so update tps:
6347 trw_layer_cancel_tps_of_track ( vtl, trk );
6349 // Inform user how much was deleted as it's not obvious from the normal view
6351 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6352 g_snprintf(str, 64, tmp_str, removed);
6353 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6355 vik_layer_emit_update ( VIK_LAYER(vtl) );
6359 * Delete adjacent track points with the same timestamp
6360 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6362 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6364 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6366 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6367 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6369 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6374 gulong removed = vik_track_remove_same_time_points ( trk );
6376 // Track has been updated so update tps:
6377 trw_layer_cancel_tps_of_track ( vtl, trk );
6379 // Inform user how much was deleted as it's not obvious from the normal view
6381 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6382 g_snprintf(str, 64, tmp_str, removed);
6383 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6385 vik_layer_emit_update ( VIK_LAYER(vtl) );
6391 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6393 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6395 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6396 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6398 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6403 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6405 vik_layer_emit_update ( VIK_LAYER(vtl) );
6408 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6410 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6412 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6413 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6415 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6420 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6422 vik_layer_emit_update ( VIK_LAYER(vtl) );
6428 static void trw_layer_reverse ( menu_array_sublayer values )
6430 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6432 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6433 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6435 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6440 vik_track_reverse ( track );
6442 vik_layer_emit_update ( VIK_LAYER(vtl) );
6446 * Similar to trw_layer_enum_item, but this uses a sorted method
6449 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6451 GList **list = (GList**)udata;
6452 // *list = g_list_prepend(*all, key); //unsorted method
6453 // Sort named list alphabetically
6454 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6459 * Now Waypoint specific sort
6461 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6463 GList **list = (GList**)udata;
6464 // Sort named list alphabetically
6465 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6469 * Track specific sort
6471 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6473 GList **list = (GList**)udata;
6474 // Sort named list alphabetically
6475 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6480 gboolean has_same_track_name;
6481 const gchar *same_track_name;
6482 } same_track_name_udata;
6484 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6486 const gchar* namea = (const gchar*) aa;
6487 const gchar* nameb = (const gchar*) bb;
6490 gint result = strcmp ( namea, nameb );
6492 if ( result == 0 ) {
6493 // Found two names the same
6494 same_track_name_udata *user_data = udata;
6495 user_data->has_same_track_name = TRUE;
6496 user_data->same_track_name = namea;
6499 // Leave ordering the same
6504 * Find out if any tracks have the same name in this hash table
6506 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6508 // Sort items by name, then compare if any next to each other are the same
6510 GList *track_names = NULL;
6511 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6514 if ( ! track_names )
6517 same_track_name_udata udata;
6518 udata.has_same_track_name = FALSE;
6520 // Use sort routine to traverse list comparing items
6521 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6522 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6523 // Still no tracks...
6527 return udata.has_same_track_name;
6531 * Force unqiue track names for the track table specified
6532 * Note the panel is a required parameter to enable the update of the names displayed
6533 * Specify if on tracks or else on routes
6535 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6537 // . Search list for an instance of repeated name
6538 // . get track of this name
6539 // . create new name
6540 // . rename track & update equiv. treeview iter
6541 // . repeat until all different
6543 same_track_name_udata udata;
6545 GList *track_names = NULL;
6546 udata.has_same_track_name = FALSE;
6547 udata.same_track_name = NULL;
6549 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6552 if ( ! track_names )
6555 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6557 // Still no tracks...
6558 if ( ! dummy_list1 )
6561 while ( udata.has_same_track_name ) {
6563 // Find a track with the same name
6566 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6568 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6572 g_critical("Houston, we've had a problem.");
6573 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6574 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6579 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6580 vik_track_set_name ( trk, newname );
6586 // Need want key of it for treeview update
6587 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6589 if ( trkf && udataU.uuid ) {
6593 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6595 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6598 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6600 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6602 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6606 // Start trying to find same names again...
6608 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6609 udata.has_same_track_name = FALSE;
6610 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6612 // No tracks any more - give up searching
6613 if ( ! dummy_list2 )
6614 udata.has_same_track_name = FALSE;
6618 vik_layers_panel_emit_update ( vlp );
6621 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6623 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6626 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6627 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6628 iter = &(vtl->tracks_iter);
6629 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6631 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6632 iter = &(vtl->routes_iter);
6633 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6635 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6636 iter = &(vtl->waypoints_iter);
6637 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6641 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6644 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6646 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6649 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6650 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6651 iter = &(vtl->tracks_iter);
6652 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6654 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6655 iter = &(vtl->routes_iter);
6656 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6658 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6659 iter = &(vtl->waypoints_iter);
6660 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6664 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6670 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6672 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6675 // Ensure list of track names offered is unique
6676 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6677 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6678 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6679 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6685 // Sort list alphabetically for better presentation
6686 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6689 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6693 // Get list of items to delete from the user
6694 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6697 _("Delete Selection"),
6698 _("Select tracks to delete"));
6701 // Delete requested tracks
6702 // since specificly requested, IMHO no need for extra confirmation
6703 if ( delete_list ) {
6705 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6706 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6707 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6709 g_list_free(delete_list);
6710 vik_layer_emit_update( VIK_LAYER(vtl) );
6717 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6719 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6722 // Ensure list of track names offered is unique
6723 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6724 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6725 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6726 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6732 // Sort list alphabetically for better presentation
6733 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6736 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6740 // Get list of items to delete from the user
6741 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6744 _("Delete Selection"),
6745 _("Select routes to delete") );
6748 // Delete requested routes
6749 // since specificly requested, IMHO no need for extra confirmation
6750 if ( delete_list ) {
6752 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6753 // This deletes first route it finds of that name (but uniqueness is enforced above)
6754 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6756 g_list_free(delete_list);
6757 vik_layer_emit_update( VIK_LAYER(vtl) );
6762 gboolean has_same_waypoint_name;
6763 const gchar *same_waypoint_name;
6764 } same_waypoint_name_udata;
6766 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6768 const gchar* namea = (const gchar*) aa;
6769 const gchar* nameb = (const gchar*) bb;
6772 gint result = strcmp ( namea, nameb );
6774 if ( result == 0 ) {
6775 // Found two names the same
6776 same_waypoint_name_udata *user_data = udata;
6777 user_data->has_same_waypoint_name = TRUE;
6778 user_data->same_waypoint_name = namea;
6781 // Leave ordering the same
6786 * Find out if any waypoints have the same name in this layer
6788 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6790 // Sort items by name, then compare if any next to each other are the same
6792 GList *waypoint_names = NULL;
6793 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6796 if ( ! waypoint_names )
6799 same_waypoint_name_udata udata;
6800 udata.has_same_waypoint_name = FALSE;
6802 // Use sort routine to traverse list comparing items
6803 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6804 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6805 // Still no waypoints...
6809 return udata.has_same_waypoint_name;
6813 * Force unqiue waypoint names for this layer
6814 * Note the panel is a required parameter to enable the update of the names displayed
6816 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6818 // . Search list for an instance of repeated name
6819 // . get waypoint of this name
6820 // . create new name
6821 // . rename waypoint & update equiv. treeview iter
6822 // . repeat until all different
6824 same_waypoint_name_udata udata;
6826 GList *waypoint_names = NULL;
6827 udata.has_same_waypoint_name = FALSE;
6828 udata.same_waypoint_name = NULL;
6830 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6833 if ( ! waypoint_names )
6836 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6838 // Still no waypoints...
6839 if ( ! dummy_list1 )
6842 while ( udata.has_same_waypoint_name ) {
6844 // Find a waypoint with the same name
6845 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6849 g_critical("Houston, we've had a problem.");
6850 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6851 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6856 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6858 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6860 // Start trying to find same names again...
6861 waypoint_names = NULL;
6862 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6863 udata.has_same_waypoint_name = FALSE;
6864 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6866 // No waypoints any more - give up searching
6867 if ( ! dummy_list2 )
6868 udata.has_same_waypoint_name = FALSE;
6872 vik_layers_panel_emit_update ( vlp );
6878 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6880 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6883 // Ensure list of waypoint names offered is unique
6884 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6885 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6886 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6887 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6893 // Sort list alphabetically for better presentation
6894 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6896 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6900 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6902 // Get list of items to delete from the user
6903 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6906 _("Delete Selection"),
6907 _("Select waypoints to delete"));
6910 // Delete requested waypoints
6911 // since specificly requested, IMHO no need for extra confirmation
6912 if ( delete_list ) {
6914 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6915 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6916 trw_layer_delete_waypoint_by_name (vtl, l->data);
6918 g_list_free(delete_list);
6920 trw_layer_calculate_bounds_waypoints ( vtl );
6921 vik_layer_emit_update( VIK_LAYER(vtl) );
6929 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6931 vik_treeview_item_toggle_visible ( vt, it );
6937 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6939 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6945 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6947 wp->visible = GPOINTER_TO_INT (on_off);
6953 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6955 wp->visible = !wp->visible;
6961 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
6963 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6964 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6965 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6966 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6968 vik_layer_emit_update ( VIK_LAYER(vtl) );
6974 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
6976 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6977 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6978 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6979 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6981 vik_layer_emit_update ( VIK_LAYER(vtl) );
6987 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
6989 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6990 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6991 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6993 vik_layer_emit_update ( VIK_LAYER(vtl) );
6999 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7001 trk->visible = GPOINTER_TO_INT (on_off);
7007 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7009 trk->visible = !trk->visible;
7015 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7017 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7018 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7019 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7020 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7022 vik_layer_emit_update ( VIK_LAYER(vtl) );
7028 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7030 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7031 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7032 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7033 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7035 vik_layer_emit_update ( VIK_LAYER(vtl) );
7041 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7043 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7044 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7045 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7047 vik_layer_emit_update ( VIK_LAYER(vtl) );
7053 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7055 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7056 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7057 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7058 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7060 vik_layer_emit_update ( VIK_LAYER(vtl) );
7066 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7068 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7069 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7070 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7071 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7073 vik_layer_emit_update ( VIK_LAYER(vtl) );
7079 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7081 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7082 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7083 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7085 vik_layer_emit_update ( VIK_LAYER(vtl) );
7089 * vik_trw_layer_build_waypoint_list_t:
7091 * Helper function to construct a list of #vik_trw_waypoint_list_t
7093 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7095 GList *waypoints_and_layers = NULL;
7096 // build waypoints_and_layers list
7097 while ( waypoints ) {
7098 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7099 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7101 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7102 waypoints = g_list_next ( waypoints );
7104 return waypoints_and_layers;
7108 * trw_layer_create_waypoint_list:
7110 * Create the latest list of waypoints with the associated layer(s)
7111 * Although this will always be from a single layer here
7113 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7115 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7116 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7118 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7122 * trw_layer_analyse_close:
7124 * Stuff to do on dialog closure
7126 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7128 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7129 gtk_widget_destroy ( dialog );
7130 vtl->tracks_analysis_dialog = NULL;
7134 * vik_trw_layer_build_track_list_t:
7136 * Helper function to construct a list of #vik_trw_track_list_t
7138 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7140 GList *tracks_and_layers = NULL;
7141 // build tracks_and_layers list
7143 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7144 vtdl->trk = VIK_TRACK(tracks->data);
7146 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7147 tracks = g_list_next ( tracks );
7149 return tracks_and_layers;
7153 * trw_layer_create_track_list:
7155 * Create the latest list of tracks with the associated layer(s)
7156 * Although this will always be from a single layer here
7158 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7160 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7161 GList *tracks = NULL;
7162 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7163 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7165 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7167 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7170 static void trw_layer_tracks_stats ( menu_array_layer values )
7172 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7173 // There can only be one!
7174 if ( vtl->tracks_analysis_dialog )
7177 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7178 VIK_LAYER(vtl)->name,
7180 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7181 trw_layer_create_track_list,
7182 trw_layer_analyse_close );
7188 static void trw_layer_routes_stats ( menu_array_layer values )
7190 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7191 // There can only be one!
7192 if ( vtl->tracks_analysis_dialog )
7195 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7196 VIK_LAYER(vtl)->name,
7198 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7199 trw_layer_create_track_list,
7200 trw_layer_analyse_close );
7203 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7205 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7206 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7208 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7211 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7213 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7214 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7217 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7218 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7222 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7224 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7225 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7228 if ( !strncmp(wp->comment, "http", 4) ) {
7229 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7230 } else if ( !strncmp(wp->description, "http", 4) ) {
7231 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7235 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7237 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7239 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7241 // No actual change to the name supplied
7243 if (strcmp(newname, wp->name) == 0 )
7246 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7249 // An existing waypoint has been found with the requested name
7250 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7251 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7256 // Update WP name and refresh the treeview
7257 vik_waypoint_set_name (wp, newname);
7259 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7260 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7262 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7267 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7269 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7271 // No actual change to the name supplied
7273 if (strcmp(newname, trk->name) == 0)
7276 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7279 // An existing track has been found with the requested name
7280 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7281 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7285 // Update track name and refresh GUI parts
7286 vik_track_set_name (trk, newname);
7288 // Update any subwindows that could be displaying this track which has changed name
7289 // Only one Track Edit Window
7290 if ( l->current_tp_track == trk && l->tpwin ) {
7291 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7293 // Property Dialog of the track
7294 vik_trw_layer_propwin_update ( trk );
7296 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7297 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7299 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7304 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7306 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7308 // No actual change to the name supplied
7310 if (strcmp(newname, trk->name) == 0)
7313 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7316 // An existing track has been found with the requested name
7317 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7318 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7322 // Update track name and refresh GUI parts
7323 vik_track_set_name (trk, newname);
7325 // Update any subwindows that could be displaying this track which has changed name
7326 // Only one Track Edit Window
7327 if ( l->current_tp_track == trk && l->tpwin ) {
7328 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7330 // Property Dialog of the track
7331 vik_trw_layer_propwin_update ( trk );
7333 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7334 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7336 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7343 static gboolean is_valid_geocache_name ( gchar *str )
7345 gint len = strlen ( str );
7346 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]));
7349 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7351 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7352 a_acquire_set_filter_track ( trk );
7355 #ifdef VIK_CONFIG_GOOGLE
7356 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7358 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7359 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7362 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7364 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7366 gchar *escaped = uri_escape ( tr->comment );
7367 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7368 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7375 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7376 /* viewpoint is now available instead */
7377 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7379 static menu_array_sublayer pass_along;
7381 gboolean rv = FALSE;
7383 pass_along[MA_VTL] = l;
7384 pass_along[MA_VLP] = vlp;
7385 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7386 pass_along[MA_SUBLAYER_ID] = sublayer;
7387 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7388 pass_along[MA_VVP] = vvp;
7389 pass_along[MA_TV_ITER] = iter;
7390 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7392 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7396 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7398 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7399 gtk_widget_show ( item );
7401 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7402 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7403 if (tr && tr->property_dialog)
7404 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7406 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7407 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7408 if (tr && tr->property_dialog)
7409 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7412 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7413 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7414 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7415 gtk_widget_show ( item );
7417 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7418 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7419 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7420 gtk_widget_show ( item );
7422 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7424 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7425 gtk_widget_show ( item );
7427 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7429 // Always create separator as now there is always at least the transform menu option
7430 item = gtk_menu_item_new ();
7431 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7432 gtk_widget_show ( item );
7434 /* could be a right-click using the tool */
7435 if ( vlp != NULL ) {
7436 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7437 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7438 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7439 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7440 gtk_widget_show ( item );
7443 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7445 if ( wp && wp->name ) {
7446 if ( is_valid_geocache_name ( wp->name ) ) {
7447 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7449 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7450 gtk_widget_show ( item );
7452 #ifdef VIK_CONFIG_GEOTAG
7453 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7455 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7456 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7457 gtk_widget_show ( item );
7461 if ( wp && wp->image )
7463 // Set up image paramater
7464 pass_along[MA_MISC] = wp->image;
7466 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7467 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
7468 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7469 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7470 gtk_widget_show ( item );
7472 #ifdef VIK_CONFIG_GEOTAG
7473 GtkWidget *geotag_submenu = gtk_menu_new ();
7474 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7475 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7476 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7477 gtk_widget_show ( item );
7478 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7480 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7482 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7483 gtk_widget_show ( item );
7485 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7486 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7487 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7488 gtk_widget_show ( item );
7494 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7495 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7496 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7499 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7500 gtk_widget_show ( item );
7506 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7507 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7509 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7510 gtk_widget_show ( item );
7511 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7512 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7513 gtk_widget_set_sensitive ( item, TRUE );
7515 gtk_widget_set_sensitive ( item, FALSE );
7518 item = gtk_menu_item_new ();
7519 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7520 gtk_widget_show ( item );
7523 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7526 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7527 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7529 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7530 gtk_widget_show ( item );
7533 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7535 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7536 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7537 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7538 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7539 gtk_widget_show ( item );
7541 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7544 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7545 gtk_widget_show ( item );
7547 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7550 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7551 gtk_widget_show ( item );
7553 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7556 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7557 gtk_widget_show ( item );
7559 GtkWidget *vis_submenu = gtk_menu_new ();
7560 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7561 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7562 gtk_widget_show ( item );
7563 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7565 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7566 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7568 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7569 gtk_widget_show ( item );
7571 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7572 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7574 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7575 gtk_widget_show ( item );
7577 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7578 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7580 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7581 gtk_widget_show ( item );
7583 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7584 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7585 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7586 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7589 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7593 if ( l->current_track && !l->current_track->is_route ) {
7594 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7595 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7596 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7597 gtk_widget_show ( item );
7599 item = gtk_menu_item_new ();
7600 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7601 gtk_widget_show ( item );
7604 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7605 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7606 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7607 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7608 gtk_widget_show ( item );
7610 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7613 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7614 gtk_widget_show ( item );
7615 // Make it available only when a new track *not* already in progress
7616 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7618 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7619 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7621 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7622 gtk_widget_show ( item );
7624 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7625 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7626 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7627 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7628 gtk_widget_show ( item );
7630 GtkWidget *vis_submenu = gtk_menu_new ();
7631 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7632 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7633 gtk_widget_show ( item );
7634 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7636 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7637 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7638 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7639 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7640 gtk_widget_show ( item );
7642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7645 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7646 gtk_widget_show ( item );
7648 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7651 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7653 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7654 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7655 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7656 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7657 gtk_widget_show ( item );
7659 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7662 gtk_widget_show ( item );
7665 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7669 if ( l->current_track && l->current_track->is_route ) {
7670 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7671 // Reuse finish track method
7672 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7673 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7674 gtk_widget_show ( item );
7676 item = gtk_menu_item_new ();
7677 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7678 gtk_widget_show ( item );
7681 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7682 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7683 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7684 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7685 gtk_widget_show ( item );
7687 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7688 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7689 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7690 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7691 gtk_widget_show ( item );
7692 // Make it available only when a new track *not* already in progress
7693 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7695 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7696 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7697 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7698 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7699 gtk_widget_show ( item );
7701 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7702 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7703 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7704 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7705 gtk_widget_show ( item );
7707 GtkWidget *vis_submenu = gtk_menu_new ();
7708 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7709 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7710 gtk_widget_show ( item );
7711 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7713 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7716 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7717 gtk_widget_show ( item );
7719 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7722 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7723 gtk_widget_show ( item );
7725 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7726 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7727 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7728 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7730 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7731 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7733 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7735 gtk_widget_show ( item );
7737 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7738 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7739 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7740 gtk_widget_show ( item );
7744 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7745 GtkWidget *submenu_sort = gtk_menu_new ();
7746 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7747 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7748 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7749 gtk_widget_show ( item );
7750 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7752 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7753 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7754 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7755 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7756 gtk_widget_show ( item );
7758 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7760 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7761 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7762 gtk_widget_show ( item );
7765 GtkWidget *upload_submenu = gtk_menu_new ();
7767 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7769 item = gtk_menu_item_new ();
7770 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7771 gtk_widget_show ( item );
7773 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7774 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7775 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7776 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7777 if ( l->current_track ) {
7778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7779 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7780 gtk_widget_show ( item );
7783 item = gtk_menu_item_new ();
7784 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7785 gtk_widget_show ( item );
7788 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7789 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7791 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7794 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7795 gtk_widget_show ( item );
7797 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7798 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7799 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7800 gtk_widget_show ( item );
7802 GtkWidget *goto_submenu;
7803 goto_submenu = gtk_menu_new ();
7804 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7805 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7806 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7807 gtk_widget_show ( item );
7808 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7810 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7811 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7812 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7813 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7814 gtk_widget_show ( item );
7816 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7820 gtk_widget_show ( item );
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7825 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7826 gtk_widget_show ( item );
7828 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7829 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7831 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7832 gtk_widget_show ( item );
7834 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7835 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7837 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7838 gtk_widget_show ( item );
7840 // Routes don't have speeds
7841 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7842 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7843 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7844 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7845 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7846 gtk_widget_show ( item );
7849 GtkWidget *combine_submenu;
7850 combine_submenu = gtk_menu_new ();
7851 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7852 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7853 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7854 gtk_widget_show ( item );
7855 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7857 // Routes don't have times or segments...
7858 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7859 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7860 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7861 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7862 gtk_widget_show ( item );
7864 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7865 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7866 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7867 gtk_widget_show ( item );
7870 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7871 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7872 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7873 gtk_widget_show ( item );
7875 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7876 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7878 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7881 gtk_widget_show ( item );
7883 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7884 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7886 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7888 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7889 gtk_widget_show ( item );
7891 GtkWidget *split_submenu;
7892 split_submenu = gtk_menu_new ();
7893 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7895 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7896 gtk_widget_show ( item );
7897 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7899 // Routes don't have times or segments...
7900 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7901 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7904 gtk_widget_show ( item );
7906 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7907 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7909 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7910 gtk_widget_show ( item );
7913 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7915 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7916 gtk_widget_show ( item );
7918 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7920 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7921 gtk_widget_show ( item );
7922 // Make it available only when a trackpoint is selected.
7923 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7925 GtkWidget *insert_submenu = gtk_menu_new ();
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7928 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7929 gtk_widget_show ( item );
7930 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7932 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7934 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7935 gtk_widget_show ( item );
7936 // Make it available only when a point is selected
7937 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7939 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7941 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7942 gtk_widget_show ( item );
7943 // Make it available only when a point is selected
7944 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7946 GtkWidget *delete_submenu;
7947 delete_submenu = gtk_menu_new ();
7948 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7949 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7950 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7951 gtk_widget_show ( item );
7952 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7954 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7955 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7957 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7958 gtk_widget_show ( item );
7959 // Make it available only when a point is selected
7960 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7962 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7964 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7965 gtk_widget_show ( item );
7967 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7969 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7970 gtk_widget_show ( item );
7972 GtkWidget *transform_submenu;
7973 transform_submenu = gtk_menu_new ();
7974 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7976 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7977 gtk_widget_show ( item );
7978 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7980 GtkWidget *dem_submenu;
7981 dem_submenu = gtk_menu_new ();
7982 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7983 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
7984 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7985 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7987 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7989 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7990 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7991 gtk_widget_show ( item );
7993 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7995 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7996 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7997 gtk_widget_show ( item );
7999 GtkWidget *smooth_submenu;
8000 smooth_submenu = gtk_menu_new ();
8001 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8002 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8003 gtk_widget_show ( item );
8004 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8006 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8007 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8008 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8009 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8010 gtk_widget_show ( item );
8012 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8014 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8015 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8016 gtk_widget_show ( item );
8018 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8019 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8021 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8022 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8024 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8025 gtk_widget_show ( item );
8027 // Routes don't have timestamps - so this is only available for tracks
8028 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8029 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8031 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8032 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8033 gtk_widget_show ( item );
8036 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8037 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8039 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8040 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8042 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8043 gtk_widget_show ( item );
8045 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8046 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8049 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8050 gtk_widget_show ( item );
8053 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8056 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8058 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8059 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
8060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8061 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8062 gtk_widget_show ( item );
8065 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8066 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8068 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8071 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8072 gtk_widget_show ( item );
8074 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8075 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8077 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8078 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8080 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8081 gtk_widget_show ( item );
8083 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8084 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8085 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
8086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8087 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8088 gtk_widget_show ( item );
8091 // ATM can't upload a single waypoint but can do waypoints to a GPS
8092 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8093 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8095 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8096 gtk_widget_show ( item );
8097 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8099 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8102 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8103 gtk_widget_show ( item );
8107 #ifdef VIK_CONFIG_GOOGLE
8108 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8110 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8111 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8113 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8114 gtk_widget_show ( item );
8118 // Some things aren't usable with routes
8119 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8120 #ifdef VIK_CONFIG_OPENSTREETMAP
8121 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8122 // Convert internal pointer into track
8123 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8124 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8126 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8127 gtk_widget_show ( item );
8130 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8133 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8134 gtk_widget_show ( item );
8136 /* ATM This function is only available via the layers panel, due to needing a vlp */
8138 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8139 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8140 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8142 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8143 gtk_widget_show ( item );
8147 #ifdef VIK_CONFIG_GEOTAG
8148 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8151 gtk_widget_show ( item );
8155 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8156 // Only show on viewport popmenu when a trackpoint is selected
8157 if ( ! vlp && l->current_tpl ) {
8159 item = gtk_menu_item_new ();
8160 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8161 gtk_widget_show ( item );
8163 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8164 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8166 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8167 gtk_widget_show ( item );
8171 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8172 GtkWidget *transform_submenu;
8173 transform_submenu = gtk_menu_new ();
8174 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8176 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8177 gtk_widget_show ( item );
8178 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8180 GtkWidget *dem_submenu;
8181 dem_submenu = gtk_menu_new ();
8182 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8183 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
8184 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8185 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8187 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8189 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8190 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8191 gtk_widget_show ( item );
8193 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8194 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8195 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8196 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8197 gtk_widget_show ( item );
8200 gtk_widget_show_all ( GTK_WIDGET(menu) );
8205 // TODO: Probably better to rework this track manipulation in viktrack.c
8206 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8209 if (!vtl->current_tpl)
8212 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8213 VikTrackpoint *tp_other = NULL;
8216 if (!vtl->current_tpl->prev)
8218 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8220 if (!vtl->current_tpl->next)
8222 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8225 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8228 VikTrackpoint *tp_new = vik_trackpoint_new();
8229 struct LatLon ll_current, ll_other;
8230 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8231 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8233 /* main positional interpolation */
8234 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8235 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8237 /* Now other properties that can be interpolated */
8238 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8240 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8241 /* Note here the division is applied to each part, then added
8242 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8243 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8244 tp_new->has_timestamp = TRUE;
8247 if (tp_current->speed != NAN && tp_other->speed != NAN)
8248 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8250 /* TODO - improve interpolation of course, as it may not be correct.
8251 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8252 [similar applies if value is in radians] */
8253 if (tp_current->course != NAN && tp_other->course != NAN)
8254 tp_new->course = (tp_current->course + tp_other->course)/2;
8256 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8258 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8259 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8261 // Otherwise try routes
8262 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8266 gint index = g_list_index ( trk->trackpoints, tp_current );
8270 // NB no recalculation of bounds since it is inserted between points
8271 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8276 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8282 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8286 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8288 if ( vtl->current_tpl )
8290 vtl->current_tpl = NULL;
8291 vtl->current_tp_track = NULL;
8292 vtl->current_tp_id = NULL;
8293 vik_layer_emit_update(VIK_LAYER(vtl));
8297 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8299 g_assert ( vtl->tpwin != NULL );
8300 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8301 trw_layer_cancel_current_tp ( vtl, TRUE );
8303 if ( vtl->current_tpl == NULL )
8306 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8308 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8309 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8311 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8313 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8315 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8319 trw_layer_trackpoint_selected_delete ( vtl, tr );
8321 if ( vtl->current_tpl )
8322 // Reset dialog with the available adjacent trackpoint
8323 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8325 vik_layer_emit_update(VIK_LAYER(vtl));
8327 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8329 if ( vtl->current_tp_track )
8330 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8331 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8333 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8335 if ( vtl->current_tp_track )
8336 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8337 vik_layer_emit_update(VIK_LAYER(vtl));
8339 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8341 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8342 vik_layer_emit_update(VIK_LAYER(vtl));
8344 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8345 vik_layer_emit_update(VIK_LAYER(vtl));
8349 * trw_layer_dialog_shift:
8350 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8352 * Try to reposition a dialog if it's over the specified coord
8353 * so to not obscure the item of interest
8355 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8357 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8359 // Attempt force dialog to be shown so we can find out where it is more reliably...
8360 while ( gtk_events_pending() )
8361 gtk_main_iteration ();
8363 // get parent window position & size
8364 gint win_pos_x, win_pos_y;
8365 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8367 gint win_size_x, win_size_y;
8368 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8370 // get own dialog size
8371 gint dia_size_x, dia_size_y;
8372 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8374 // get own dialog position
8375 gint dia_pos_x, dia_pos_y;
8376 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8378 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8379 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8381 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8383 gint vp_xx, vp_yy; // In viewport pixels
8384 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8386 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8390 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8392 // Transform Viewport pixels into absolute pixels
8393 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8394 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8396 // Is dialog over the point (to within an ^^ edge value)
8397 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8398 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8402 gint hh = vik_viewport_get_height ( vvp );
8404 // Consider the difference in viewport to the full window
8405 gint offset_y = dest_y;
8406 // Add difference between dialog and window sizes
8407 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8409 if ( vp_yy > hh/2 ) {
8410 // Point in bottom half, move window to top half
8411 gtk_window_move ( dialog, dia_pos_x, offset_y );
8414 // Point in top half, move dialog down
8415 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8419 // Shift left<->right
8420 gint ww = vik_viewport_get_width ( vvp );
8422 // Consider the difference in viewport to the full window
8423 gint offset_x = dest_x;
8424 // Add difference between dialog and window sizes
8425 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8427 if ( vp_xx > ww/2 ) {
8428 // Point on right, move window to left
8429 gtk_window_move ( dialog, offset_x, dia_pos_y );
8432 // Point on left, move right
8433 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8441 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8445 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8446 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8447 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8448 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8450 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8452 if ( vtl->current_tpl ) {
8453 // get tp pixel position
8454 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8456 // Shift up<->down to try not to obscure the trackpoint.
8457 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8461 if ( vtl->current_tpl )
8462 if ( vtl->current_tp_track )
8463 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8464 /* set layer name and TP data */
8467 /***************************************************************************
8469 ***************************************************************************/
8471 /*** Utility data structures and functions ****/
8475 gint closest_x, closest_y;
8476 gboolean draw_images;
8477 gpointer *closest_wp_id;
8478 VikWaypoint *closest_wp;
8484 gint closest_x, closest_y;
8485 gpointer closest_track_id;
8486 VikTrackpoint *closest_tp;
8492 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8498 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8500 // If waypoint has an image then use the image size to select
8501 if ( params->draw_images && wp->image ) {
8502 gint slackx, slacky;
8503 slackx = wp->image_width / 2;
8504 slacky = wp->image_height / 2;
8506 if ( x <= params->x + slackx && x >= params->x - slackx
8507 && y <= params->y + slacky && y >= params->y - slacky ) {
8508 params->closest_wp_id = id;
8509 params->closest_wp = wp;
8510 params->closest_x = x;
8511 params->closest_y = y;
8514 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8515 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8516 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8518 params->closest_wp_id = id;
8519 params->closest_wp = wp;
8520 params->closest_x = x;
8521 params->closest_y = y;
8525 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8527 GList *tpl = t->trackpoints;
8533 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8539 tp = VIK_TRACKPOINT(tpl->data);
8541 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8543 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8544 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8545 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8547 params->closest_track_id = id;
8548 params->closest_tp = tp;
8549 params->closest_tpl = tpl;
8550 params->closest_x = x;
8551 params->closest_y = y;
8557 // ATM: Leave this as 'Track' only.
8558 // Not overly bothered about having a snap to route trackpoint capability
8559 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8561 TPSearchParams params;
8565 params.closest_track_id = NULL;
8566 params.closest_tp = NULL;
8567 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8568 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8569 return params.closest_tp;
8572 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8574 WPSearchParams params;
8578 params.draw_images = vtl->drawimages;
8579 params.closest_wp = NULL;
8580 params.closest_wp_id = NULL;
8581 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8582 return params.closest_wp;
8586 // Some forward declarations
8587 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8588 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8589 static void marker_end_move ( tool_ed_t *t );
8592 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8596 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8598 // Here always allow snapping back to the original location
8599 // this is useful when one decides not to move the thing afterall
8600 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8603 if ( event->state & GDK_CONTROL_MASK )
8605 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8607 new_coord = tp->coord;
8611 if ( event->state & GDK_SHIFT_MASK )
8613 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8615 new_coord = wp->coord;
8619 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8621 marker_moveto ( t, x, y );
8628 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8630 if ( t->holding && event->button == 1 )
8633 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8636 if ( event->state & GDK_CONTROL_MASK )
8638 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8640 new_coord = tp->coord;
8644 if ( event->state & GDK_SHIFT_MASK )
8646 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8648 new_coord = wp->coord;
8651 marker_end_move ( t );
8653 // Determine if working on a waypoint or a trackpoint
8654 if ( t->is_waypoint ) {
8655 // Update waypoint position
8656 vtl->current_wp->coord = new_coord;
8657 trw_layer_calculate_bounds_waypoints ( vtl );
8658 // Reset waypoint pointer
8659 vtl->current_wp = NULL;
8660 vtl->current_wp_id = NULL;
8663 if ( vtl->current_tpl ) {
8664 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8666 if ( vtl->current_tp_track )
8667 vik_track_calculate_bounds ( vtl->current_tp_track );
8670 if ( vtl->current_tp_track )
8671 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8672 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8676 vik_layer_emit_update ( VIK_LAYER(vtl) );
8683 Returns true if a waypoint or track is found near the requested event position for this particular layer
8684 The item found is automatically selected
8685 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8687 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8689 if ( event->button != 1 )
8692 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8695 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8699 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8701 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8703 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8704 WPSearchParams wp_params;
8705 wp_params.vvp = vvp;
8706 wp_params.x = event->x;
8707 wp_params.y = event->y;
8708 wp_params.draw_images = vtl->drawimages;
8709 wp_params.closest_wp_id = NULL;
8710 wp_params.closest_wp = NULL;
8712 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8714 if ( wp_params.closest_wp ) {
8717 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8719 // Too easy to move it so must be holding shift to start immediately moving it
8720 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8721 if ( event->state & GDK_SHIFT_MASK ||
8722 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8723 // Put into 'move buffer'
8724 // NB vvp & vw already set in tet
8725 tet->vtl = (gpointer)vtl;
8726 tet->is_waypoint = TRUE;
8728 marker_begin_move (tet, event->x, event->y);
8731 vtl->current_wp = wp_params.closest_wp;
8732 vtl->current_wp_id = wp_params.closest_wp_id;
8734 if ( event->type == GDK_2BUTTON_PRESS ) {
8735 if ( vtl->current_wp->image ) {
8736 menu_array_sublayer values;
8737 values[MA_VTL] = vtl;
8738 values[MA_MISC] = vtl->current_wp->image;
8739 trw_layer_show_picture ( values );
8743 vik_layer_emit_update ( VIK_LAYER(vtl) );
8749 // Used for both track and route lists
8750 TPSearchParams tp_params;
8751 tp_params.vvp = vvp;
8752 tp_params.x = event->x;
8753 tp_params.y = event->y;
8754 tp_params.closest_track_id = NULL;
8755 tp_params.closest_tp = NULL;
8756 tp_params.closest_tpl = NULL;
8757 tp_params.bbox = bbox;
8759 if (vtl->tracks_visible) {
8760 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8762 if ( tp_params.closest_tp ) {
8764 // Always select + highlight the track
8765 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8767 tet->is_waypoint = FALSE;
8769 // Select the Trackpoint
8770 // Can move it immediately when control held or it's the previously selected tp
8771 if ( event->state & GDK_CONTROL_MASK ||
8772 vtl->current_tpl == tp_params.closest_tpl ) {
8773 // Put into 'move buffer'
8774 // NB vvp & vw already set in tet
8775 tet->vtl = (gpointer)vtl;
8776 marker_begin_move (tet, event->x, event->y);
8779 vtl->current_tpl = tp_params.closest_tpl;
8780 vtl->current_tp_id = tp_params.closest_track_id;
8781 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8783 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8786 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8788 vik_layer_emit_update ( VIK_LAYER(vtl) );
8793 // Try again for routes
8794 if (vtl->routes_visible) {
8795 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8797 if ( tp_params.closest_tp ) {
8799 // Always select + highlight the track
8800 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8802 tet->is_waypoint = FALSE;
8804 // Select the Trackpoint
8805 // Can move it immediately when control held or it's the previously selected tp
8806 if ( event->state & GDK_CONTROL_MASK ||
8807 vtl->current_tpl == tp_params.closest_tpl ) {
8808 // Put into 'move buffer'
8809 // NB vvp & vw already set in tet
8810 tet->vtl = (gpointer)vtl;
8811 marker_begin_move (tet, event->x, event->y);
8814 vtl->current_tpl = tp_params.closest_tpl;
8815 vtl->current_tp_id = tp_params.closest_track_id;
8816 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8818 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8821 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8823 vik_layer_emit_update ( VIK_LAYER(vtl) );
8828 /* these aren't the droids you're looking for */
8829 vtl->current_wp = NULL;
8830 vtl->current_wp_id = NULL;
8831 trw_layer_cancel_current_tp ( vtl, FALSE );
8834 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8839 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8841 if ( event->button != 3 )
8844 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8847 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8850 /* Post menu for the currently selected item */
8852 /* See if a track is selected */
8853 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8854 if ( track && track->visible ) {
8856 if ( track->name ) {
8858 if ( vtl->track_right_click_menu )
8859 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8861 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8868 if ( track->is_route )
8869 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8871 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8873 if ( trkf && udataU.uuid ) {
8876 if ( track->is_route )
8877 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8879 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8881 trw_layer_sublayer_add_menu_items ( vtl,
8882 vtl->track_right_click_menu,
8884 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8890 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8896 /* See if a waypoint is selected */
8897 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8898 if ( waypoint && waypoint->visible ) {
8899 if ( waypoint->name ) {
8901 if ( vtl->wp_right_click_menu )
8902 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8904 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8907 udata.wp = waypoint;
8910 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8912 if ( wpf && udata.uuid ) {
8913 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8915 trw_layer_sublayer_add_menu_items ( vtl,
8916 vtl->wp_right_click_menu,
8918 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8923 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8932 /* background drawing hook, to be passed the viewport */
8933 static gboolean tool_sync_done = TRUE;
8935 static gboolean tool_sync(gpointer data)
8937 VikViewport *vvp = data;
8938 gdk_threads_enter();
8939 vik_viewport_sync(vvp);
8940 tool_sync_done = TRUE;
8941 gdk_threads_leave();
8945 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8948 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8949 gdk_gc_set_function ( t->gc, GDK_INVERT );
8950 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8951 vik_viewport_sync(t->vvp);
8956 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8958 VikViewport *vvp = t->vvp;
8959 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8960 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8964 if (tool_sync_done) {
8965 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8966 tool_sync_done = FALSE;
8970 static void marker_end_move ( tool_ed_t *t )
8972 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8973 g_object_unref ( t->gc );
8977 /*** Edit waypoint ****/
8979 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8981 tool_ed_t *t = g_new(tool_ed_t, 1);
8987 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8992 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8994 WPSearchParams params;
8995 tool_ed_t *t = data;
8996 VikViewport *vvp = t->vvp;
8998 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9005 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9008 if ( vtl->current_wp && vtl->current_wp->visible )
9010 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9012 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9014 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9015 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9017 if ( event->button == 3 )
9018 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9020 marker_begin_move(t, event->x, event->y);
9027 params.x = event->x;
9028 params.y = event->y;
9029 params.draw_images = vtl->drawimages;
9030 params.closest_wp_id = NULL;
9031 params.closest_wp = NULL;
9032 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9033 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9035 marker_begin_move(t, event->x, event->y);
9038 else if ( params.closest_wp )
9040 if ( event->button == 3 )
9041 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9043 vtl->waypoint_rightclick = FALSE;
9045 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9047 vtl->current_wp = params.closest_wp;
9048 vtl->current_wp_id = params.closest_wp_id;
9050 /* could make it so don't update if old WP is off screen and new is null but oh well */
9051 vik_layer_emit_update ( VIK_LAYER(vtl) );
9055 vtl->current_wp = NULL;
9056 vtl->current_wp_id = NULL;
9057 vtl->waypoint_rightclick = FALSE;
9058 vik_layer_emit_update ( VIK_LAYER(vtl) );
9062 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9064 tool_ed_t *t = data;
9065 VikViewport *vvp = t->vvp;
9067 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9072 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9075 if ( event->state & GDK_CONTROL_MASK )
9077 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9079 new_coord = tp->coord;
9083 if ( event->state & GDK_SHIFT_MASK )
9085 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9086 if ( wp && wp != vtl->current_wp )
9087 new_coord = wp->coord;
9092 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9094 marker_moveto ( t, x, y );
9101 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9103 tool_ed_t *t = data;
9104 VikViewport *vvp = t->vvp;
9106 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9109 if ( t->holding && event->button == 1 )
9112 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9115 if ( event->state & GDK_CONTROL_MASK )
9117 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9119 new_coord = tp->coord;
9123 if ( event->state & GDK_SHIFT_MASK )
9125 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9126 if ( wp && wp != vtl->current_wp )
9127 new_coord = wp->coord;
9130 marker_end_move ( t );
9132 vtl->current_wp->coord = new_coord;
9134 trw_layer_calculate_bounds_waypoints ( vtl );
9135 vik_layer_emit_update ( VIK_LAYER(vtl) );
9138 /* PUT IN RIGHT PLACE!!! */
9139 if ( event->button == 3 && vtl->waypoint_rightclick )
9141 if ( vtl->wp_right_click_menu )
9142 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9143 if ( vtl->current_wp ) {
9144 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9145 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 );
9146 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9148 vtl->waypoint_rightclick = FALSE;
9153 /*** New track ****/
9155 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9162 GdkDrawable *drawable;
9168 * Draw specified pixmap
9170 static gboolean draw_sync ( gpointer data )
9172 draw_sync_t *ds = (draw_sync_t*) data;
9173 // Sometimes don't want to draw
9174 // normally because another update has taken precedent such as panning the display
9175 // which means this pixmap is no longer valid
9176 if ( ds->vtl->draw_sync_do ) {
9177 gdk_threads_enter();
9178 gdk_draw_drawable (ds->drawable,
9181 0, 0, 0, 0, -1, -1);
9182 ds->vtl->draw_sync_done = TRUE;
9183 gdk_threads_leave();
9189 static gchar* distance_string (gdouble distance)
9193 /* draw label with distance */
9194 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9195 switch (dist_units) {
9196 case VIK_UNITS_DISTANCE_MILES:
9197 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9198 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9199 } else if (distance < 1609.4) {
9200 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9202 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9206 // VIK_UNITS_DISTANCE_KILOMETRES
9207 if (distance >= 1000 && distance < 100000) {
9208 g_sprintf(str, "%3.2f km", distance/1000.0);
9209 } else if (distance < 1000) {
9210 g_sprintf(str, "%d m", (int)distance);
9212 g_sprintf(str, "%d km", (int)distance/1000);
9216 return g_strdup (str);
9220 * Actually set the message in statusbar
9222 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9224 // Only show elevation data when track has some elevation properties
9225 gchar str_gain_loss[64];
9226 str_gain_loss[0] = '\0';
9227 gchar str_last_step[64];
9228 str_last_step[0] = '\0';
9229 gchar *str_total = distance_string (distance);
9231 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9232 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9233 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9235 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9238 if ( last_step > 0 ) {
9239 gchar *tmp = distance_string (last_step);
9240 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9244 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9246 // Write with full gain/loss information
9247 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9248 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9250 g_free ( str_total );
9254 * Figure out what information should be set in the statusbar and then write it
9256 static void update_statusbar ( VikTrwLayer *vtl )
9258 // Get elevation data
9259 gdouble elev_gain, elev_loss;
9260 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9262 /* Find out actual distance of current track */
9263 gdouble distance = vik_track_get_length (vtl->current_track);
9265 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9269 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9271 /* if we haven't sync'ed yet, we don't have time to do more. */
9272 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9273 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9275 static GdkPixmap *pixmap = NULL;
9277 // Need to check in case window has been resized
9278 w1 = vik_viewport_get_width(vvp);
9279 h1 = vik_viewport_get_height(vvp);
9281 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9283 gdk_drawable_get_size (pixmap, &w2, &h2);
9284 if (w1 != w2 || h1 != h2) {
9285 g_object_unref ( G_OBJECT ( pixmap ) );
9286 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9289 // Reset to background
9290 gdk_draw_drawable (pixmap,
9291 vtl->current_track_newpoint_gc,
9292 vik_viewport_get_pixmap(vvp),
9293 0, 0, 0, 0, -1, -1);
9295 draw_sync_t *passalong;
9298 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9300 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9301 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9302 // thus when we come to reset to the background it would include what we have already drawn!!
9303 gdk_draw_line ( pixmap,
9304 vtl->current_track_newpoint_gc,
9305 x1, y1, event->x, event->y );
9306 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9308 /* Find out actual distance of current track */
9309 gdouble distance = vik_track_get_length (vtl->current_track);
9311 // Now add distance to where the pointer is //
9314 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9315 vik_coord_to_latlon ( &coord, &ll );
9316 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9317 distance = distance + last_step;
9319 // Get elevation data
9320 gdouble elev_gain, elev_loss;
9321 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9323 // Adjust elevation data (if available) for the current pointer position
9325 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9326 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9327 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9328 // Adjust elevation of last track point
9329 if ( elev_new > last_tpt->altitude )
9331 elev_gain += elev_new - last_tpt->altitude;
9334 elev_loss += last_tpt->altitude - elev_new;
9339 // Display of the distance 'tooltip' during track creation is controlled by a preference
9341 if ( a_vik_get_create_track_tooltip() ) {
9343 gchar *str = distance_string (distance);
9345 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9346 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9347 pango_layout_set_text (pl, str, -1);
9349 pango_layout_get_pixel_size ( pl, &wd, &hd );
9352 // offset from cursor a bit depending on font size
9356 // Create a background block to make the text easier to read over the background map
9357 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9358 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9359 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9361 g_object_unref ( G_OBJECT ( pl ) );
9362 g_object_unref ( G_OBJECT ( background_block_gc ) );
9366 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9367 passalong->vtl = vtl;
9368 passalong->pixmap = pixmap;
9369 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9370 passalong->gc = vtl->current_track_newpoint_gc;
9374 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9376 // Update statusbar with full gain/loss information
9377 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9379 // draw pixmap when we have time to
9380 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9381 vtl->draw_sync_done = FALSE;
9382 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9384 return VIK_LAYER_TOOL_ACK;
9387 // NB vtl->current_track must be valid
9388 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9391 if ( vtl->current_track->trackpoints ) {
9392 // TODO rework this...
9393 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9394 GList *last = g_list_last(vtl->current_track->trackpoints);
9395 g_free ( last->data );
9396 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9398 vik_track_calculate_bounds ( vtl->current_track );
9402 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9404 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9405 vtl->current_track = NULL;
9406 vik_layer_emit_update ( VIK_LAYER(vtl) );
9408 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9409 undo_trackpoint_add ( vtl );
9410 update_statusbar ( vtl );
9411 vik_layer_emit_update ( VIK_LAYER(vtl) );
9418 * Common function to handle trackpoint button requests on either a route or a track
9419 * . enables adding a point via normal click
9420 * . enables removal of last point via right click
9421 * . finishing of the track or route via double clicking
9423 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9427 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9430 if ( event->button == 2 ) {
9431 // As the display is panning, the new track pixmap is now invalid so don't draw it
9432 // otherwise this drawing done results in flickering back to an old image
9433 vtl->draw_sync_do = FALSE;
9437 if ( event->button == 3 )
9439 if ( !vtl->current_track )
9441 undo_trackpoint_add ( vtl );
9442 update_statusbar ( vtl );
9443 vik_layer_emit_update ( VIK_LAYER(vtl) );
9447 if ( event->type == GDK_2BUTTON_PRESS )
9449 /* subtract last (duplicate from double click) tp then end */
9450 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9452 /* undo last, then end */
9453 undo_trackpoint_add ( vtl );
9454 vtl->current_track = NULL;
9456 vik_layer_emit_update ( VIK_LAYER(vtl) );
9460 tp = vik_trackpoint_new();
9461 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9463 /* snap to other TP */
9464 if ( event->state & GDK_CONTROL_MASK )
9466 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9468 tp->coord = other_tp->coord;
9471 tp->newsegment = FALSE;
9472 tp->has_timestamp = FALSE;
9475 if ( vtl->current_track ) {
9476 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9477 /* Auto attempt to get elevation from DEM data (if it's available) */
9478 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9481 vtl->ct_x1 = vtl->ct_x2;
9482 vtl->ct_y1 = vtl->ct_y2;
9483 vtl->ct_x2 = event->x;
9484 vtl->ct_y2 = event->y;
9486 vik_layer_emit_update ( VIK_LAYER(vtl) );
9490 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9492 // ----------------------------------------------------- if current is a route - switch to new track
9493 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9495 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9496 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9498 new_track_create_common ( vtl, name );
9504 return tool_new_track_or_route_click ( vtl, event, vvp );
9507 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9509 if ( event->button == 2 ) {
9510 // Pan moving ended - enable potential point drawing again
9511 vtl->draw_sync_do = TRUE;
9512 vtl->draw_sync_done = TRUE;
9516 /*** New route ****/
9518 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9523 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9525 // -------------------------- if current is a track - switch to new route
9526 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9528 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9529 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9530 new_route_create_common ( vtl, name );
9536 return tool_new_track_or_route_click ( vtl, event, vvp );
9539 /*** New waypoint ****/
9541 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9546 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9549 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9551 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9552 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9553 trw_layer_calculate_bounds_waypoints ( vtl );
9554 vik_layer_emit_update ( VIK_LAYER(vtl) );
9560 /*** Edit trackpoint ****/
9562 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9564 tool_ed_t *t = g_new(tool_ed_t, 1);
9570 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9575 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9577 tool_ed_t *t = data;
9578 VikViewport *vvp = t->vvp;
9579 TPSearchParams params;
9580 /* OUTDATED DOCUMENTATION:
9581 find 5 pixel range on each side. then put these UTM, and a pointer
9582 to the winning track name (and maybe the winning track itself), and a
9583 pointer to the winning trackpoint, inside an array or struct. pass
9584 this along, do a foreach on the tracks which will do a foreach on the
9587 params.x = event->x;
9588 params.y = event->y;
9589 params.closest_track_id = NULL;
9590 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9591 params.closest_tp = NULL;
9592 params.closest_tpl = NULL;
9593 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9595 if ( event->button != 1 )
9598 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9601 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9604 if ( vtl->current_tpl )
9606 /* 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.) */
9607 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9608 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9613 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9615 if ( current_tr->visible &&
9616 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9617 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9618 marker_begin_move ( t, event->x, event->y );
9624 if ( vtl->tracks_visible )
9625 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9627 if ( params.closest_tp )
9629 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9630 vtl->current_tpl = params.closest_tpl;
9631 vtl->current_tp_id = params.closest_track_id;
9632 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9633 trw_layer_tpwin_init ( vtl );
9634 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9635 vik_layer_emit_update ( VIK_LAYER(vtl) );
9639 if ( vtl->routes_visible )
9640 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9642 if ( params.closest_tp )
9644 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9645 vtl->current_tpl = params.closest_tpl;
9646 vtl->current_tp_id = params.closest_track_id;
9647 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9648 trw_layer_tpwin_init ( vtl );
9649 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9650 vik_layer_emit_update ( VIK_LAYER(vtl) );
9654 /* these aren't the droids you're looking for */
9658 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9660 tool_ed_t *t = data;
9661 VikViewport *vvp = t->vvp;
9663 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9669 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9672 if ( event->state & GDK_CONTROL_MASK )
9674 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9675 if ( tp && tp != vtl->current_tpl->data )
9676 new_coord = tp->coord;
9678 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9681 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9682 marker_moveto ( t, x, y );
9690 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9692 tool_ed_t *t = data;
9693 VikViewport *vvp = t->vvp;
9695 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9697 if ( event->button != 1)
9702 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9705 if ( event->state & GDK_CONTROL_MASK )
9707 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9708 if ( tp && tp != vtl->current_tpl->data )
9709 new_coord = tp->coord;
9712 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9713 if ( vtl->current_tp_track )
9714 vik_track_calculate_bounds ( vtl->current_tp_track );
9716 marker_end_move ( t );
9718 /* diff dist is diff from orig */
9720 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9722 vik_layer_emit_update ( VIK_LAYER(vtl) );
9729 /*** Route Finder ***/
9730 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9735 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9738 if ( !vtl ) return FALSE;
9739 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9740 if ( event->button == 3 && vtl->route_finder_current_track ) {
9742 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9744 vtl->route_finder_coord = *new_end;
9746 vik_layer_emit_update ( VIK_LAYER(vtl) );
9747 /* remove last ' to:...' */
9748 if ( vtl->route_finder_current_track->comment ) {
9749 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9750 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9751 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9752 last_to - vtl->route_finder_current_track->comment - 1);
9753 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9758 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9759 struct LatLon start, end;
9761 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9762 vik_coord_to_latlon ( &(tmp), &end );
9763 vtl->route_finder_coord = tmp; /* for continuations */
9765 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9766 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9767 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9769 vtl->route_finder_check_added_track = TRUE;
9770 vtl->route_finder_started = FALSE;
9773 vik_routing_default_find ( vtl, start, end);
9775 /* see if anything was done -- a track was added or appended to */
9776 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9777 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 ) );
9778 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9779 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9780 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9781 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9784 if ( vtl->route_finder_added_track )
9785 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9787 vtl->route_finder_added_track = NULL;
9788 vtl->route_finder_check_added_track = FALSE;
9789 vtl->route_finder_append = FALSE;
9791 vik_layer_emit_update ( VIK_LAYER(vtl) );
9793 vtl->route_finder_started = TRUE;
9794 vtl->route_finder_coord = tmp;
9795 vtl->route_finder_current_track = NULL;
9800 /*** Show picture ****/
9802 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9807 /* Params are: vvp, event, last match found or NULL */
9808 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9810 if ( wp->image && wp->visible )
9812 gint x, y, slackx, slacky;
9813 GdkEventButton *event = (GdkEventButton *) params[1];
9815 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9816 slackx = wp->image_width / 2;
9817 slacky = wp->image_height / 2;
9818 if ( x <= event->x + slackx && x >= event->x - slackx
9819 && y <= event->y + slacky && y >= event->y - slacky )
9821 params[2] = wp->image; /* we've found a match. however continue searching
9822 * since we want to find the last match -- that
9823 * is, the match that was drawn last. */
9828 static void trw_layer_show_picture ( menu_array_sublayer values )
9830 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9832 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9835 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9836 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9837 g_free ( quoted_file );
9838 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9840 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() );
9841 g_error_free ( err );
9844 #endif /* WINDOWS */
9847 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9849 gpointer params[3] = { vvp, event, NULL };
9850 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9852 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9855 static menu_array_sublayer values;
9856 values[MA_VTL] = vtl;
9857 values[MA_MISC] = params[2];
9858 trw_layer_show_picture ( values );
9859 return TRUE; /* found a match */
9862 return FALSE; /* go through other layers, searching for a match */
9865 /***************************************************************************
9867 ***************************************************************************/
9870 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9872 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9873 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9876 /* Structure for thumbnail creating data used in the background thread */
9878 VikTrwLayer *vtl; // Layer needed for redrawing
9879 GSList *pics; // Image list
9880 } thumbnail_create_thread_data;
9882 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9884 guint total = g_slist_length(tctd->pics), done = 0;
9885 while ( tctd->pics )
9887 a_thumbnails_create ( (gchar *) tctd->pics->data );
9888 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9890 return -1; /* Abort thread */
9892 tctd->pics = tctd->pics->next;
9895 // Redraw to show the thumbnails as they are now created
9896 if ( IS_VIK_LAYER(tctd->vtl) )
9897 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9902 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9904 while ( tctd->pics )
9906 g_free ( tctd->pics->data );
9907 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9912 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9914 if ( ! vtl->has_verified_thumbnails )
9916 GSList *pics = NULL;
9917 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9920 gint len = g_slist_length ( pics );
9921 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9922 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9925 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9927 (vik_thr_func) create_thumbnails_thread,
9929 (vik_thr_free_func) thumbnail_create_thread_free,
9937 static const gchar* my_track_colors ( gint ii )
9939 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9951 // Fast and reliable way of returning a colour
9952 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9955 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9957 GHashTableIter iter;
9958 gpointer key, value;
9962 g_hash_table_iter_init ( &iter, vtl->tracks );
9964 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9966 // Tracks get a random spread of colours if not already assigned
9967 if ( ! VIK_TRACK(value)->has_color ) {
9968 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9969 VIK_TRACK(value)->color = vtl->track_color;
9971 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9973 VIK_TRACK(value)->has_color = TRUE;
9976 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9979 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9985 g_hash_table_iter_init ( &iter, vtl->routes );
9987 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9989 // Routes get an intermix of reds
9990 if ( ! VIK_TRACK(value)->has_color ) {
9992 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9994 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9995 VIK_TRACK(value)->has_color = TRUE;
9998 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10005 * (Re)Calculate the bounds of the waypoints in this layer,
10006 * This should be called whenever waypoints are changed
10008 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10010 struct LatLon topleft = { 0.0, 0.0 };
10011 struct LatLon bottomright = { 0.0, 0.0 };
10014 GHashTableIter iter;
10015 gpointer key, value;
10017 g_hash_table_iter_init ( &iter, vtl->waypoints );
10019 // Set bounds to first point
10020 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10021 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10022 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10025 // Ensure there is another point...
10026 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10028 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10030 // See if this point increases the bounds.
10031 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10033 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10034 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10035 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10036 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10040 vtl->waypoints_bbox.north = topleft.lat;
10041 vtl->waypoints_bbox.east = bottomright.lon;
10042 vtl->waypoints_bbox.south = bottomright.lat;
10043 vtl->waypoints_bbox.west = topleft.lon;
10046 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10048 vik_track_calculate_bounds ( trk );
10051 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10053 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10054 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10057 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10059 if ( ! VIK_LAYER(vtl)->vt )
10062 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10063 if ( g_hash_table_size (vtl->tracks) > 1 )
10064 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10066 if ( g_hash_table_size (vtl->routes) > 1 )
10067 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10069 if ( g_hash_table_size (vtl->waypoints) > 1 )
10070 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10073 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10075 if ( VIK_LAYER(vtl)->realized )
10076 trw_layer_verify_thumbnails ( vtl, vvp );
10077 trw_layer_track_alloc_colors ( vtl );
10079 trw_layer_calculate_bounds_waypoints ( vtl );
10080 trw_layer_calculate_bounds_tracks ( vtl );
10082 // Apply treeview sort after loading all the tracks for this layer
10083 // (rather than sorted insert on each individual track additional)
10084 // and after subsequent changes to the properties as the specified order may have changed.
10085 // since the sorting of a treeview section is now very quick
10086 // NB sorting is also performed after every name change as well to maintain the list order
10087 trw_layer_sort_all ( vtl );
10089 // Setting metadata time if not otherwise set
10090 if ( vtl->metadata ) {
10092 gboolean need_to_set_time = TRUE;
10093 if ( vtl->metadata->timestamp ) {
10094 need_to_set_time = FALSE;
10095 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10096 need_to_set_time = TRUE;
10099 if ( need_to_set_time ) {
10100 // Could rewrite this as a general get first time of a TRW Layer function
10101 GTimeVal timestamp;
10102 timestamp.tv_usec = 0;
10103 gboolean has_timestamp = FALSE;
10106 gl = g_hash_table_get_values ( vtl->tracks );
10107 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10108 gl = g_list_first ( gl );
10110 // Check times of tracks
10112 // Only need to check the first track as they have been sorted by time
10113 VikTrack *trk = (VikTrack*)gl->data;
10114 // Assume trackpoints already sorted by time
10115 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10116 if ( tpt && tpt->has_timestamp ) {
10117 timestamp.tv_sec = tpt->timestamp;
10118 has_timestamp = TRUE;
10120 g_list_free ( gl );
10123 if ( !has_timestamp ) {
10124 // 'Last' resort - current time
10125 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10126 g_get_current_time ( ×tamp );
10128 // Check times of waypoints
10129 gl = g_hash_table_get_values ( vtl->waypoints );
10131 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10132 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10133 if ( wpt->has_timestamp ) {
10134 if ( timestamp.tv_sec > wpt->timestamp ) {
10135 timestamp.tv_sec = wpt->timestamp;
10136 has_timestamp = TRUE;
10140 g_list_free ( gl );
10143 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10148 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10150 return vtl->coord_mode;
10154 * Uniquify the whole layer
10155 * Also requires the layers panel as the names shown there need updating too
10156 * Returns whether the operation was successful or not
10158 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10160 if ( vtl && vlp ) {
10161 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10162 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10163 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10169 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10171 vik_coord_convert ( &(wp->coord), *dest_mode );
10174 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10176 vik_track_convert ( tr, *dest_mode );
10179 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10181 if ( vtl->coord_mode != dest_mode )
10183 vtl->coord_mode = dest_mode;
10184 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10185 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10186 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10190 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10192 vtl->menu_selection = selection;
10195 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10197 return (vtl->menu_selection);
10200 /* ----------- Downloading maps along tracks --------------- */
10202 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10204 /* TODO: calculating based on current size of viewport */
10205 const gdouble w_at_zoom_0_125 = 0.0013;
10206 const gdouble h_at_zoom_0_125 = 0.0011;
10207 gdouble zoom_factor = zoom_level/0.125;
10209 wh->lat = h_at_zoom_0_125 * zoom_factor;
10210 wh->lon = w_at_zoom_0_125 * zoom_factor;
10212 return 0; /* all OK */
10215 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10217 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10218 (dist->lat >= ABS(to->north_south - from->north_south)))
10221 VikCoord *coord = g_malloc(sizeof(VikCoord));
10222 coord->mode = VIK_COORD_LATLON;
10224 if (ABS(gradient) < 1) {
10225 if (from->east_west > to->east_west)
10226 coord->east_west = from->east_west - dist->lon;
10228 coord->east_west = from->east_west + dist->lon;
10229 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10231 if (from->north_south > to->north_south)
10232 coord->north_south = from->north_south - dist->lat;
10234 coord->north_south = from->north_south + dist->lat;
10235 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10241 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10243 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10244 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10246 VikCoord *next = from;
10248 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10250 list = g_list_prepend(list, next);
10256 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10258 typedef struct _Rect {
10263 #define GLRECT(iter) ((Rect *)((iter)->data))
10266 GList *rects_to_download = NULL;
10269 if (get_download_area_width(vvp, zoom_level, &wh))
10272 GList *iter = tr->trackpoints;
10276 gboolean new_map = TRUE;
10277 VikCoord *cur_coord, tl, br;
10280 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10282 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10283 rect = g_malloc(sizeof(Rect));
10286 rect->center = *cur_coord;
10287 rects_to_download = g_list_prepend(rects_to_download, rect);
10292 gboolean found = FALSE;
10293 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10294 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10305 GList *fillins = NULL;
10306 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10307 /* seems that ATM the function get_next_coord works only for LATLON */
10308 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10309 /* fill-ins for far apart points */
10310 GList *cur_rect, *next_rect;
10311 for (cur_rect = rects_to_download;
10312 (next_rect = cur_rect->next) != NULL;
10313 cur_rect = cur_rect->next) {
10314 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10315 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10316 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10320 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10323 GList *iter = fillins;
10325 cur_coord = (VikCoord *)(iter->data);
10326 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10327 rect = g_malloc(sizeof(Rect));
10330 rect->center = *cur_coord;
10331 rects_to_download = g_list_prepend(rects_to_download, rect);
10336 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10337 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10341 for (iter = fillins; iter; iter = iter->next)
10342 g_free(iter->data);
10343 g_list_free(fillins);
10345 if (rects_to_download) {
10346 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10347 g_free(rect_iter->data);
10348 g_list_free(rects_to_download);
10352 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10356 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10357 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10358 gint selected_zoom, default_zoom;
10360 VikTrwLayer *vtl = values[MA_VTL];
10361 VikLayersPanel *vlp = values[MA_VLP];
10363 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10364 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10366 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10370 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10372 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10373 int num_maps = g_list_length(vmls);
10376 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10380 // Convert from list of vmls to list of names. Allowing the user to select one of them
10381 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10382 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10384 gchar **np = map_names;
10385 VikMapsLayer **lp = map_layers;
10387 for (i = 0; i < num_maps; i++) {
10388 vml = (VikMapsLayer *)(vmls->data);
10390 *np++ = vik_maps_layer_get_map_label(vml);
10393 // Mark end of the array lists
10397 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10398 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10399 if (cur_zoom == zoom_vals[default_zoom])
10402 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10404 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10407 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10410 for (i = 0; i < num_maps; i++)
10411 g_free(map_names[i]);
10413 g_free(map_layers);
10419 /**** lowest waypoint number calculation ***/
10420 static gint highest_wp_number_name_to_number(const gchar *name) {
10421 if ( strlen(name) == 3 ) {
10422 int n = atoi(name);
10423 if ( n < 100 && name[0] != '0' )
10425 if ( n < 10 && name[0] != '0' )
10433 static void highest_wp_number_reset(VikTrwLayer *vtl)
10435 vtl->highest_wp_number = -1;
10438 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10440 /* if is bigger that top, add it */
10441 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10442 if ( new_wp_num > vtl->highest_wp_number )
10443 vtl->highest_wp_number = new_wp_num;
10446 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10448 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10449 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10450 if ( vtl->highest_wp_number == old_wp_num ) {
10452 vtl->highest_wp_number--;
10454 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10455 /* search down until we find something that *does* exist */
10457 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10458 vtl->highest_wp_number--;
10459 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10464 /* get lowest unused number */
10465 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10468 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10470 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10471 return g_strdup(buf);
10475 * trw_layer_create_track_list_both:
10477 * Create the latest list of tracks and routes
10479 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10481 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10482 GList *tracks = NULL;
10483 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10484 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10486 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10489 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10491 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10493 gchar *title = NULL;
10494 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10495 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10497 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10499 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10503 static void trw_layer_track_list_dialog ( menu_array_layer values )
10505 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10507 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10508 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10512 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10514 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10516 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10517 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );