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_tpwin.h"
37 #include "viktrwlayer_propwin.h"
38 #include "viktrwlayer_analysis.h"
39 #include "viktrwlayer_tracklist.h"
40 #include "viktrwlayer_waypointlist.h"
41 #ifdef VIK_CONFIG_GEOTAG
42 #include "viktrwlayer_geotag.h"
43 #include "geotag_exif.h"
45 #include "garminsymbols.h"
46 #include "thumbnails.h"
47 #include "background.h"
52 #include "geonamessearch.h"
53 #ifdef VIK_CONFIG_OPENSTREETMAP
54 #include "osm-traces.h"
57 #include "datasources.h"
58 #include "datasource_gps.h"
59 #include "vikexttool_datasources.h"
63 #include "vikrouting.h"
65 #include "icons/icons.h"
79 #include <gdk/gdkkeysyms.h>
81 #include <glib/gstdio.h>
82 #include <glib/gi18n.h>
84 #define VIK_TRW_LAYER_TRACK_GC 6
85 #define VIK_TRW_LAYER_TRACK_GCS 10
86 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
87 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
88 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
89 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
90 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
91 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
93 #define DRAWMODE_BY_TRACK 0
94 #define DRAWMODE_BY_SPEED 1
95 #define DRAWMODE_ALL_SAME_COLOR 2
96 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
97 // as we are (re)calculating the colour for every point
102 /* this is how it knows when you click if you are clicking close to a trackpoint. */
103 #define TRACKPOINT_SIZE_APPROX 5
104 #define WAYPOINT_SIZE_APPROX 5
106 #define MIN_STOP_LENGTH 15
107 #define MAX_STOP_LENGTH 86400
108 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
109 /* this is multiplied by user-inputted value from 1-100. */
111 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115 FS_XX_SMALL = 0, // 'xx-small'
118 FS_MEDIUM, // DEFAULT
125 struct _VikTrwLayer {
128 GHashTable *tracks_iters;
130 GHashTable *routes_iters;
131 GHashTable *waypoints_iters;
132 GHashTable *waypoints;
133 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
134 gboolean tracks_visible, routes_visible, waypoints_visible;
135 LatLonBBox waypoints_bbox;
137 gboolean track_draw_labels;
140 guint8 drawpoints_size;
141 guint8 drawelevation;
142 guint8 elevation_factor;
146 guint8 drawdirections;
147 guint8 drawdirections_size;
148 guint8 line_thickness;
149 guint8 bg_line_thickness;
150 vik_layer_sort_order_t track_sort_order;
153 VikTRWMetadata *metadata;
155 PangoLayout *tracklabellayout;
156 font_size_t track_font_size;
157 gchar *track_fsize_str;
161 gboolean wp_draw_symbols;
162 font_size_t wp_font_size;
164 vik_layer_sort_order_t wp_sort_order;
166 gdouble track_draw_speed_factor;
168 GdkGC *track_1color_gc;
169 GdkColor track_color;
170 GdkGC *current_track_gc;
171 // Separate GC for a track's potential new point as drawn via separate method
172 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
173 GdkGC *current_track_newpoint_gc;
174 GdkGC *track_bg_gc; GdkColor track_bg_color;
175 GdkGC *waypoint_gc; GdkColor waypoint_color;
176 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
177 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
179 GdkFont *waypoint_font;
180 VikTrack *current_track; // ATM shared between new tracks and new routes
181 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
182 gboolean draw_sync_done;
183 gboolean draw_sync_do;
185 VikCoordMode coord_mode;
187 /* wp editing tool */
188 VikWaypoint *current_wp;
189 gpointer current_wp_id;
191 gboolean waypoint_rightclick;
193 /* track editing tool */
195 VikTrack *current_tp_track;
196 gpointer current_tp_id;
197 VikTrwLayerTpwin *tpwin;
199 /* track editing tool -- more specifically, moving tps */
202 /* route finder tool */
203 gboolean route_finder_started;
204 VikCoord route_finder_coord;
205 gboolean route_finder_check_added_track;
206 VikTrack *route_finder_added_track;
207 VikTrack *route_finder_current_track;
208 gboolean route_finder_append;
215 guint16 image_cache_size;
217 /* for waypoint text */
218 PangoLayout *wplabellayout;
220 gboolean has_verified_thumbnails;
222 GtkMenu *wp_right_click_menu;
223 GtkMenu *track_right_click_menu;
226 VikStdLayerMenuItem menu_selection;
228 gint highest_wp_number;
231 GtkWidget *tracks_analysis_dialog;
234 /* A caached waypoint image. */
237 gchar *image; /* filename */
240 struct DrawingParams {
245 guint16 width, height;
246 gdouble cc; // Cosine factor in track directions
247 gdouble ss; // Sine factor in track directions
248 const VikCoord *center;
249 gboolean one_zone, lat_lon;
250 gdouble ce1, ce2, cn1, cn2;
254 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
259 MA_SUBTYPE, // OR END for Layer only
268 typedef gpointer menu_array_layer[2];
269 typedef gpointer menu_array_sublayer[MA_LAST];
271 static void trw_layer_delete_item ( menu_array_sublayer values );
272 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
273 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
275 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
276 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
278 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
279 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
281 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
282 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
284 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
285 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
286 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
287 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
288 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
289 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
290 static void trw_layer_goto_track_center ( menu_array_sublayer values );
291 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
292 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
293 static void trw_layer_merge_with_other ( menu_array_sublayer values );
294 static void trw_layer_append_track ( menu_array_sublayer values );
295 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
296 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
297 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
298 static void trw_layer_split_segments ( menu_array_sublayer values );
299 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
300 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
301 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
302 static void trw_layer_reverse ( menu_array_sublayer values );
303 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
304 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
305 static void trw_layer_show_picture ( menu_array_sublayer values );
306 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
308 static void trw_layer_centerize ( menu_array_layer values );
309 static void trw_layer_auto_view ( menu_array_layer values );
310 static void trw_layer_export ( menu_array_layer values, const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
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 ( menu_array_layer values, const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3250 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3251 GtkWidget *file_selector;
3253 gboolean failed = FALSE;
3254 file_selector = gtk_file_chooser_dialog_new (title,
3256 GTK_FILE_CHOOSER_ACTION_SAVE,
3257 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3258 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3260 gchar *cwd = g_get_current_dir();
3262 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3266 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3268 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3270 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3271 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ||
3272 a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3274 gtk_widget_hide ( file_selector );
3275 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3276 failed = ! a_file_export ( vtl, fn, file_type, trk, TRUE );
3277 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3281 gtk_widget_destroy ( file_selector );
3283 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("The filename you requested could not be opened for writing.") );
3286 static void trw_layer_export_gpspoint ( menu_array_layer values )
3288 trw_layer_export ( values, _("Export Layer"), vik_layer_get_name(VIK_LAYER(values[MA_VTL])), NULL, FILE_TYPE_GPSPOINT );
3291 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3293 trw_layer_export ( values, _("Export Layer"), vik_layer_get_name(VIK_LAYER(values[MA_VTL])), NULL, FILE_TYPE_GPSMAPPER );
3296 static void trw_layer_export_gpx ( menu_array_layer values )
3298 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3299 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])) );
3300 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3301 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3303 trw_layer_export ( values, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3305 g_free ( auto_save_name );
3308 static void trw_layer_export_kml ( menu_array_layer values )
3310 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3311 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])) );
3312 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3313 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3315 trw_layer_export ( values, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3317 g_free ( auto_save_name );
3321 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3324 static void trw_layer_export_external_gpx ( menu_array_layer values, const gchar* external_program )
3326 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3327 gchar *name_used = NULL;
3330 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3331 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3332 gboolean failed = ! a_file_export ( vtl, name_used, FILE_TYPE_GPX, NULL, TRUE);
3333 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3335 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not create temporary file for export.") );
3339 gchar *quoted_file = g_shell_quote ( name_used );
3340 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3341 g_free ( quoted_file );
3342 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3344 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s."), external_program );
3345 g_error_free ( err );
3349 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3350 //g_remove ( name_used );
3351 // Perhaps should be deleted when the program ends?
3352 // For now leave it to the user to delete it / use system temp cleanup methods.
3353 g_free ( name_used );
3357 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3359 trw_layer_export_external_gpx ( values, a_vik_get_external_gpx_program_1() );
3362 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3364 trw_layer_export_external_gpx ( values, a_vik_get_external_gpx_program_2() );
3367 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3369 menu_array_layer data;
3370 data[MA_VTL] = values[MA_VTL];
3371 data[MA_VLP] = values[MA_VLP];
3373 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3375 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3376 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3378 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3380 if ( !trk || !trk->name )
3383 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3384 gchar *auto_save_name = g_strdup ( trk->name );
3385 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3386 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3388 gchar *label = NULL;
3389 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3390 label = _("Export Route as GPX");
3392 label = _("Export Track as GPX");
3393 trw_layer_export ( data, label, auto_save_name, trk, FILE_TYPE_GPX );
3395 g_free ( auto_save_name );
3398 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3400 wpu_udata *user_data = udata;
3401 if ( wp == user_data->wp ) {
3402 user_data->uuid = id;
3408 static void trw_layer_goto_wp ( menu_array_layer values )
3410 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3411 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3412 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3413 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3414 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3416 GTK_RESPONSE_REJECT,
3418 GTK_RESPONSE_ACCEPT,
3421 GtkWidget *label, *entry;
3422 label = gtk_label_new(_("Waypoint Name:"));
3423 entry = gtk_entry_new();
3425 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3426 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3427 gtk_widget_show_all ( label );
3428 gtk_widget_show_all ( entry );
3430 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3432 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3434 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3435 // Find *first* wp with the given name
3436 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3439 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3442 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3443 vik_layers_panel_emit_update ( vlp );
3445 // Find and select on the side panel
3450 // Hmmm, want key of it
3451 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3453 if ( wpf && udata.uuid ) {
3454 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3455 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3464 gtk_widget_destroy ( dia );
3467 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3469 gchar *default_name = highest_wp_number_get(vtl);
3470 VikWaypoint *wp = vik_waypoint_new();
3471 gchar *returned_name;
3473 wp->coord = *def_coord;
3475 // Attempt to auto set height if DEM data is available
3476 vik_waypoint_apply_dem_data ( wp, TRUE );
3478 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3480 if ( returned_name )
3483 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3484 g_free (default_name);
3485 g_free (returned_name);
3488 g_free (default_name);
3489 vik_waypoint_free(wp);
3493 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3495 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3496 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3497 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3498 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3499 VikViewport *vvp = vik_window_viewport(vw);
3501 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3502 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3503 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3504 trw_layer_calculate_bounds_waypoints ( vtl );
3505 vik_layers_panel_emit_update ( vlp );
3508 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3510 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3511 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3512 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3514 trw_layer_find_maxmin (vtl, maxmin);
3515 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3516 trw_layer_calculate_bounds_waypoints ( vtl );
3517 vik_layers_panel_emit_update ( vlp );
3520 #ifdef VIK_CONFIG_GEOTAG
3521 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3523 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3525 // Update directly - not changing the mtime
3526 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3529 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3531 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3534 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3538 * Use code in separate file for this feature as reasonably complex
3540 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3542 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3543 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3544 // Unset so can be reverified later if necessary
3545 vtl->has_verified_thumbnails = FALSE;
3547 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3553 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3555 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3556 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3558 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3564 static void trw_layer_geotagging ( menu_array_layer values )
3566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3567 // Unset so can be reverified later if necessary
3568 vtl->has_verified_thumbnails = FALSE;
3570 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3577 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3579 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3581 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3582 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3583 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3584 VikViewport *vvp = vik_window_viewport(vw);
3586 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3590 * Acquire into this TRW Layer straight from GPS Device
3592 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3594 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3595 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3599 * Acquire into this TRW Layer from Directions
3601 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3603 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3607 * Acquire into this TRW Layer from an entered URL
3609 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3611 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3612 trw_layer_acquire ( values, &vik_datasource_url_interface );
3615 #ifdef VIK_CONFIG_OPENSTREETMAP
3617 * Acquire into this TRW Layer from OSM
3619 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3621 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3625 * Acquire into this TRW Layer from OSM for 'My' Traces
3627 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3629 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3633 #ifdef VIK_CONFIG_GEOCACHES
3635 * Acquire into this TRW Layer from Geocaching.com
3637 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3639 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3643 #ifdef VIK_CONFIG_GEOTAG
3645 * Acquire into this TRW Layer from images
3647 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3649 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3651 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3652 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3654 // Reverify thumbnails as they may have changed
3655 vtl->has_verified_thumbnails = FALSE;
3656 trw_layer_verify_thumbnails ( vtl, NULL );
3660 static void trw_layer_gps_upload ( menu_array_layer values )
3662 menu_array_sublayer data;
3664 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3666 data[MA_VTL] = values[MA_VTL];
3667 data[MA_VLP] = values[MA_VLP];
3669 trw_layer_gps_upload_any ( data );
3673 * If pass_along[3] is defined that this will upload just that track
3675 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3677 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3678 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3680 // May not actually get a track here as values[2&3] can be null
3681 VikTrack *track = NULL;
3682 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3683 gboolean xfer_all = FALSE;
3685 if ( values[MA_SUBTYPE] ) {
3687 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3688 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3691 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3692 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3695 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3698 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3702 else if ( !values[MA_CONFIRM] )
3703 xfer_all = TRUE; // i.e. whole layer
3705 if (track && !track->visible) {
3706 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3710 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3711 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3712 GTK_DIALOG_DESTROY_WITH_PARENT,
3714 GTK_RESPONSE_ACCEPT,
3716 GTK_RESPONSE_REJECT,
3719 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3720 GtkWidget *response_w = NULL;
3721 #if GTK_CHECK_VERSION (2, 20, 0)
3722 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3726 gtk_widget_grab_focus ( response_w );
3728 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3730 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3731 datasource_gps_clean_up ( dgs );
3732 gtk_widget_destroy ( dialog );
3736 // Get info from reused datasource dialog widgets
3737 gchar* protocol = datasource_gps_get_protocol ( dgs );
3738 gchar* port = datasource_gps_get_descriptor ( dgs );
3739 // NB don't free the above strings as they're references to values held elsewhere
3740 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3741 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3742 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3743 gboolean turn_off = datasource_gps_get_off ( dgs );
3745 gtk_widget_destroy ( dialog );
3747 // When called from the viewport - work the corresponding layerspanel:
3749 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3752 // Apply settings to transfer to the GPS device
3759 vik_layers_panel_get_viewport (vlp),
3768 * Acquire into this TRW Layer from any GPS Babel supported file
3770 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3772 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3773 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3774 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3775 VikViewport *vvp = vik_window_viewport(vw);
3777 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3780 static void trw_layer_new_wp ( menu_array_layer values )
3782 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3783 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3784 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3785 instead return true if you want to update. */
3786 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 ) {
3787 trw_layer_calculate_bounds_waypoints ( vtl );
3788 vik_layers_panel_emit_update ( vlp );
3792 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3794 vtl->current_track = vik_track_new();
3795 vik_track_set_defaults ( vtl->current_track );
3796 vtl->current_track->visible = TRUE;
3797 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3798 // Create track with the preferred colour from the layer properties
3799 vtl->current_track->color = vtl->track_color;
3801 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3802 vtl->current_track->has_color = TRUE;
3803 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3806 static void trw_layer_new_track ( menu_array_layer values )
3808 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3810 if ( ! vtl->current_track ) {
3811 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3812 new_track_create_common ( vtl, name );
3815 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3819 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3821 vtl->current_track = vik_track_new();
3822 vik_track_set_defaults ( vtl->current_track );
3823 vtl->current_track->visible = TRUE;
3824 vtl->current_track->is_route = TRUE;
3825 // By default make all routes red
3826 vtl->current_track->has_color = TRUE;
3827 gdk_color_parse ( "red", &vtl->current_track->color );
3828 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3831 static void trw_layer_new_route ( menu_array_layer values )
3833 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3835 if ( ! vtl->current_track ) {
3836 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3837 new_route_create_common ( vtl, name );
3839 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3843 static void trw_layer_auto_routes_view ( menu_array_layer values )
3845 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3846 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3848 if ( g_hash_table_size (vtl->routes) > 0 ) {
3849 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3850 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3851 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3852 vik_layers_panel_emit_update ( vlp );
3857 static void trw_layer_finish_track ( menu_array_layer values )
3859 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3860 vtl->current_track = NULL;
3861 vik_layer_emit_update ( VIK_LAYER(vtl) );
3864 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3866 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3867 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3869 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3870 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3871 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3872 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3873 vik_layers_panel_emit_update ( vlp );
3877 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3879 /* NB do not care if wp is visible or not */
3880 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3883 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3885 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3886 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3888 /* Only 1 waypoint - jump straight to it */
3889 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3890 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3891 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3893 /* If at least 2 waypoints - find center and then zoom to fit */
3894 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3896 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3897 maxmin[0].lat = vtl->waypoints_bbox.north;
3898 maxmin[1].lat = vtl->waypoints_bbox.south;
3899 maxmin[0].lon = vtl->waypoints_bbox.east;
3900 maxmin[1].lon = vtl->waypoints_bbox.west;
3901 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3904 vik_layers_panel_emit_update ( vlp );
3907 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3909 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3912 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3914 if ( values[MA_MISC] ) {
3915 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3916 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3920 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3922 static menu_array_layer pass_along;
3924 GtkWidget *export_submenu;
3925 pass_along[MA_VTL] = vtl;
3926 pass_along[MA_VLP] = vlp;
3928 item = gtk_menu_item_new();
3929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3930 gtk_widget_show ( item );
3932 if ( vtl->current_track ) {
3933 if ( vtl->current_track->is_route )
3934 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3936 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3937 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3938 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3939 gtk_widget_show ( item );
3942 item = gtk_menu_item_new ();
3943 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3944 gtk_widget_show ( item );
3947 /* Now with icons */
3948 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3949 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3951 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3952 gtk_widget_show ( item );
3954 GtkWidget *view_submenu = gtk_menu_new();
3955 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3956 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3957 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3958 gtk_widget_show ( item );
3959 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3961 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3963 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3964 gtk_widget_show ( item );
3966 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3968 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3969 gtk_widget_show ( item );
3971 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3973 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3974 gtk_widget_show ( item );
3976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3979 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3980 gtk_widget_show ( item );
3982 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3984 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3985 gtk_widget_show ( item );
3987 export_submenu = gtk_menu_new ();
3988 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3989 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3990 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3991 gtk_widget_show ( item );
3992 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3994 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3996 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3997 gtk_widget_show ( item );
3999 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4001 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4002 gtk_widget_show ( item );
4004 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4006 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4007 gtk_widget_show ( item );
4009 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4011 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4012 gtk_widget_show ( item );
4014 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4015 item = gtk_menu_item_new_with_mnemonic ( external1 );
4016 g_free ( external1 );
4017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4018 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4019 gtk_widget_show ( item );
4021 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4022 item = gtk_menu_item_new_with_mnemonic ( external2 );
4023 g_free ( external2 );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4025 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4026 gtk_widget_show ( item );
4028 GtkWidget *new_submenu = gtk_menu_new();
4029 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4030 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4031 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4032 gtk_widget_show(item);
4033 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4035 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4036 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4037 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4038 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4039 gtk_widget_show ( item );
4041 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4042 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4044 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4045 gtk_widget_show ( item );
4046 // Make it available only when a new track *not* already in progress
4047 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4049 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4052 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4053 gtk_widget_show ( item );
4054 // Make it available only when a new track *not* already in progress
4055 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4057 #ifdef VIK_CONFIG_GEOTAG
4058 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4060 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4061 gtk_widget_show ( item );
4064 GtkWidget *acquire_submenu = gtk_menu_new ();
4065 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4067 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4068 gtk_widget_show ( item );
4069 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4071 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4072 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4073 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4074 gtk_widget_show ( item );
4076 /* FIXME: only add menu when at least a routing engine has support for Directions */
4077 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4079 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4080 gtk_widget_show ( item );
4082 #ifdef VIK_CONFIG_OPENSTREETMAP
4083 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4085 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4086 gtk_widget_show ( item );
4088 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4090 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4091 gtk_widget_show ( item );
4094 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4096 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4097 gtk_widget_show ( item );
4099 #ifdef VIK_CONFIG_GEONAMES
4100 GtkWidget *wikipedia_submenu = gtk_menu_new();
4101 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4103 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4104 gtk_widget_show(item);
4105 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4107 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4108 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4110 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4111 gtk_widget_show ( item );
4113 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4117 gtk_widget_show ( item );
4120 #ifdef VIK_CONFIG_GEOCACHES
4121 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4123 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4124 gtk_widget_show ( item );
4127 #ifdef VIK_CONFIG_GEOTAG
4128 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4129 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4130 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4131 gtk_widget_show ( item );
4134 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4136 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4137 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4138 gtk_widget_show ( item );
4140 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4142 GtkWidget *upload_submenu = gtk_menu_new ();
4143 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4144 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4145 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4146 gtk_widget_show ( item );
4147 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4149 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4150 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4151 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4152 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4153 gtk_widget_show ( item );
4155 #ifdef VIK_CONFIG_OPENSTREETMAP
4156 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4157 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4159 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4160 gtk_widget_show ( item );
4163 GtkWidget *delete_submenu = gtk_menu_new ();
4164 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4165 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4166 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4167 gtk_widget_show ( item );
4168 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4170 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4171 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4172 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4173 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4174 gtk_widget_show ( item );
4176 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4179 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4180 gtk_widget_show ( item );
4182 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4183 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4185 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4186 gtk_widget_show ( item );
4188 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4189 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4191 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4192 gtk_widget_show ( item );
4194 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4197 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4198 gtk_widget_show ( item );
4200 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4201 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4203 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4204 gtk_widget_show ( item );
4206 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4207 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4209 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4210 gtk_widget_show ( item );
4213 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4214 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4216 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4217 gtk_widget_show ( item );
4220 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4221 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4222 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4223 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4224 gtk_widget_show ( item );
4225 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4227 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4229 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4230 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4231 gtk_widget_show ( item );
4232 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4235 // Fake Waypoint UUIDs vi simple increasing integer
4236 static guint wp_uuid = 0;
4238 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4242 vik_waypoint_set_name (wp, name);
4244 if ( VIK_LAYER(vtl)->realized )
4246 // Do we need to create the sublayer:
4247 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4248 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4251 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4253 // Visibility column always needed for waypoints
4254 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 );
4256 // Actual setting of visibility dependent on the waypoint
4257 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4259 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4261 // Sort now as post_read is not called on a realized waypoint
4262 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4265 highest_wp_number_add_wp(vtl, name);
4266 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4270 // Fake Track UUIDs vi simple increasing integer
4271 static guint tr_uuid = 0;
4273 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4277 vik_track_set_name (t, name);
4279 if ( VIK_LAYER(vtl)->realized )
4281 // Do we need to create the sublayer:
4282 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4283 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4286 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4287 // Visibility column always needed for tracks
4288 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 );
4290 // Actual setting of visibility dependent on the track
4291 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4293 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4295 // Sort now as post_read is not called on a realized track
4296 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4299 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4301 trw_layer_update_treeview ( vtl, t );
4304 // Fake Route UUIDs vi simple increasing integer
4305 static guint rt_uuid = 0;
4307 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4311 vik_track_set_name (t, name);
4313 if ( VIK_LAYER(vtl)->realized )
4315 // Do we need to create the sublayer:
4316 if ( g_hash_table_size (vtl->routes) == 0 ) {
4317 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4320 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4321 // Visibility column always needed for routes
4322 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 );
4323 // Actual setting of visibility dependent on the route
4324 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4326 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4328 // Sort now as post_read is not called on a realized route
4329 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4332 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4334 trw_layer_update_treeview ( vtl, t );
4337 /* to be called whenever a track has been deleted or may have been changed. */
4338 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4340 if (vtl->current_tp_track == trk )
4341 trw_layer_cancel_current_tp ( vtl, FALSE );
4345 * Normally this is done to due the waypoint size preference having changed
4347 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4349 GHashTableIter iter;
4350 gpointer key, value;
4353 g_hash_table_iter_init ( &iter, vtl->waypoints );
4354 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4355 VikWaypoint *wp = VIK_WAYPOINT(value);
4357 // Reapply symbol setting to update the pixbuf
4358 gchar *tmp_symbol = g_strdup ( wp->symbol );
4359 vik_waypoint_set_symbol ( wp, tmp_symbol );
4360 g_free ( tmp_symbol );
4366 * trw_layer_new_unique_sublayer_name:
4368 * Allocates a unique new name
4370 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4373 gchar *newname = g_strdup(name);
4378 switch ( sublayer_type ) {
4379 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4380 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4382 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4383 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4386 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4389 // If found a name already in use try adding 1 to it and we try again
4391 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4393 newname = new_newname;
4396 } while ( id != NULL);
4401 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4403 // No more uniqueness of name forced when loading from a file
4404 // This now makes this function a little redunant as we just flow the parameters through
4405 vik_trw_layer_add_waypoint ( vtl, name, wp );
4408 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4410 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4411 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4412 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4413 vik_track_free ( tr );
4414 vtl->route_finder_append = FALSE; /* this means we have added it */
4417 // No more uniqueness of name forced when loading from a file
4419 vik_trw_layer_add_route ( vtl, name, tr );
4421 vik_trw_layer_add_track ( vtl, name, tr );
4423 if ( vtl->route_finder_check_added_track ) {
4424 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4425 vtl->route_finder_added_track = tr;
4430 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4432 *l = g_list_append(*l, id);
4436 * Move an item from one TRW layer to another TRW layer
4438 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4440 // TODO reconsider strategy when moving within layer (if anything...)
4441 gboolean rename = ( vtl_src != vtl_dest );
4445 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4446 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4450 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4452 newname = g_strdup ( trk->name );
4454 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4455 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4457 vik_trw_layer_delete_track ( vtl_src, trk );
4460 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4461 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4465 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4467 newname = g_strdup ( trk->name );
4469 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4470 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4472 vik_trw_layer_delete_route ( vtl_src, trk );
4475 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4476 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4480 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4482 newname = g_strdup ( wp->name );
4484 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4485 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4487 trw_layer_delete_waypoint ( vtl_src, wp );
4489 // Recalculate bounds even if not renamed as maybe dragged between layers
4490 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4491 trw_layer_calculate_bounds_waypoints ( vtl_src );
4495 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4497 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4498 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4500 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4501 GList *items = NULL;
4504 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4505 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4507 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4508 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4510 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4511 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4516 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4517 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4518 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4519 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4521 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4528 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4529 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4533 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4535 trku_udata *user_data = udata;
4536 if ( trk == user_data->trk ) {
4537 user_data->uuid = id;
4543 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4545 gboolean was_visible = FALSE;
4547 if ( trk && trk->name ) {
4549 if ( trk == vtl->current_track ) {
4550 vtl->current_track = NULL;
4551 vtl->current_tp_track = NULL;
4552 vtl->current_tp_id = NULL;
4553 vtl->moving_tp = FALSE;
4556 was_visible = trk->visible;
4558 if ( trk == vtl->route_finder_current_track )
4559 vtl->route_finder_current_track = NULL;
4561 if ( trk == vtl->route_finder_added_track )
4562 vtl->route_finder_added_track = NULL;
4568 // Hmmm, want key of it
4569 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4571 if ( trkf && udata.uuid ) {
4572 /* could be current_tp, so we have to check */
4573 trw_layer_cancel_tps_of_track ( vtl, trk );
4575 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4578 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4579 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4580 g_hash_table_remove ( vtl->tracks, udata.uuid );
4582 // If last sublayer, then remove sublayer container
4583 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4584 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4592 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4594 gboolean was_visible = FALSE;
4596 if ( trk && trk->name ) {
4598 if ( trk == vtl->current_track ) {
4599 vtl->current_track = NULL;
4600 vtl->current_tp_track = NULL;
4601 vtl->current_tp_id = NULL;
4602 vtl->moving_tp = FALSE;
4605 was_visible = trk->visible;
4607 if ( trk == vtl->route_finder_current_track )
4608 vtl->route_finder_current_track = NULL;
4610 if ( trk == vtl->route_finder_added_track )
4611 vtl->route_finder_added_track = NULL;
4617 // Hmmm, want key of it
4618 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4620 if ( trkf && udata.uuid ) {
4621 /* could be current_tp, so we have to check */
4622 trw_layer_cancel_tps_of_track ( vtl, trk );
4624 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4627 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4628 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4629 g_hash_table_remove ( vtl->routes, udata.uuid );
4631 // If last sublayer, then remove sublayer container
4632 if ( g_hash_table_size (vtl->routes) == 0 ) {
4633 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4641 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4643 gboolean was_visible = FALSE;
4645 if ( wp && wp->name ) {
4647 if ( wp == vtl->current_wp ) {
4648 vtl->current_wp = NULL;
4649 vtl->current_wp_id = NULL;
4650 vtl->moving_wp = FALSE;
4653 was_visible = wp->visible;
4659 // Hmmm, want key of it
4660 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4662 if ( wpf && udata.uuid ) {
4663 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4666 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4667 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4669 highest_wp_number_remove_wp(vtl, wp->name);
4670 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4672 // If last sublayer, then remove sublayer container
4673 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4674 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4684 // Only for temporary use by trw_layer_delete_waypoint_by_name
4685 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4687 wpu_udata *user_data = udata;
4688 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4689 user_data->uuid = id;
4696 * Delete a waypoint by the given name
4697 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4698 * as there be multiple waypoints with the same name
4700 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4703 // Fake a waypoint with the given name
4704 udata.wp = vik_waypoint_new ();
4705 vik_waypoint_set_name (udata.wp, name);
4706 // Currently only the name is used in this waypoint find function
4709 // Hmmm, want key of it
4710 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4712 vik_waypoint_free (udata.wp);
4714 if ( wpf && udata.uuid )
4715 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4721 VikTrack *trk; // input
4722 gpointer uuid; // output
4725 // Only for temporary use by trw_layer_delete_track_by_name
4726 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4728 tpu_udata *user_data = udata;
4729 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4730 user_data->uuid = id;
4737 * Delete a track by the given name
4738 * NOTE: ATM this will delete the first encountered Track with the specified name
4739 * as there may be multiple tracks with the same name within the specified hash table
4741 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4744 // Fake a track with the given name
4745 udata.trk = vik_track_new ();
4746 vik_track_set_name (udata.trk, name);
4747 // Currently only the name is used in this waypoint find function
4750 // Hmmm, want key of it
4751 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4753 vik_track_free (udata.trk);
4755 if ( trkf && udata.uuid ) {
4756 // This could be a little better written...
4757 if ( vtl->tracks == ht_tracks )
4758 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4759 if ( vtl->routes == ht_tracks )
4760 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4767 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4769 vik_treeview_item_delete (vt, it );
4772 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4775 vtl->current_track = NULL;
4776 vtl->route_finder_current_track = NULL;
4777 vtl->route_finder_added_track = NULL;
4778 if (vtl->current_tp_track)
4779 trw_layer_cancel_current_tp(vtl, FALSE);
4781 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4782 g_hash_table_remove_all(vtl->routes_iters);
4783 g_hash_table_remove_all(vtl->routes);
4785 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4787 vik_layer_emit_update ( VIK_LAYER(vtl) );
4790 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4793 vtl->current_track = NULL;
4794 vtl->route_finder_current_track = NULL;
4795 vtl->route_finder_added_track = NULL;
4796 if (vtl->current_tp_track)
4797 trw_layer_cancel_current_tp(vtl, FALSE);
4799 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4800 g_hash_table_remove_all(vtl->tracks_iters);
4801 g_hash_table_remove_all(vtl->tracks);
4803 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4805 vik_layer_emit_update ( VIK_LAYER(vtl) );
4808 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4810 vtl->current_wp = NULL;
4811 vtl->current_wp_id = NULL;
4812 vtl->moving_wp = FALSE;
4814 highest_wp_number_reset(vtl);
4816 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4817 g_hash_table_remove_all(vtl->waypoints_iters);
4818 g_hash_table_remove_all(vtl->waypoints);
4820 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4822 vik_layer_emit_update ( VIK_LAYER(vtl) );
4825 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4827 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4828 // Get confirmation from the user
4829 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4830 _("Are you sure you want to delete all tracks in %s?"),
4831 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4832 vik_trw_layer_delete_all_tracks (vtl);
4835 static void trw_layer_delete_all_routes ( menu_array_layer values )
4837 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4838 // Get confirmation from the user
4839 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4840 _("Are you sure you want to delete all routes in %s?"),
4841 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4842 vik_trw_layer_delete_all_routes (vtl);
4845 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4847 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4848 // Get confirmation from the user
4849 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4850 _("Are you sure you want to delete all waypoints in %s?"),
4851 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4852 vik_trw_layer_delete_all_waypoints (vtl);
4855 static void trw_layer_delete_item ( menu_array_sublayer values )
4857 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4858 gboolean was_visible = FALSE;
4859 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4861 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4862 if ( wp && wp->name ) {
4863 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4864 // Get confirmation from the user
4865 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4866 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4867 _("Are you sure you want to delete the waypoint \"%s\"?"),
4870 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4873 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4875 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4876 if ( trk && trk->name ) {
4877 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4878 // Get confirmation from the user
4879 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4880 _("Are you sure you want to delete the track \"%s\"?"),
4883 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4888 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4889 if ( trk && trk->name ) {
4890 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4891 // Get confirmation from the user
4892 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4893 _("Are you sure you want to delete the route \"%s\"?"),
4896 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4900 vik_layer_emit_update ( VIK_LAYER(vtl) );
4904 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4906 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4908 vik_waypoint_set_name ( wp, new_name );
4910 // Now update the treeview as well
4915 // Need key of it for treeview update
4916 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4918 if ( wpf && udataU.uuid ) {
4919 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4922 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4923 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4929 * Maintain icon of waypoint in the treeview
4931 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4933 // update the treeview
4938 // Need key of it for treeview update
4939 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4941 if ( wpf && udataU.uuid ) {
4942 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4945 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4950 static void trw_layer_properties_item ( menu_array_sublayer values )
4952 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4953 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4955 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4957 if ( wp && wp->name )
4959 gboolean updated = FALSE;
4960 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4962 trw_layer_waypoint_rename ( vtl, wp, new_name );
4964 if ( updated && values[MA_TV_ITER] )
4965 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4967 if ( updated && VIK_LAYER(vtl)->visible )
4968 vik_layer_emit_update ( VIK_LAYER(vtl) );
4974 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4975 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4977 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4979 if ( tr && tr->name )
4981 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4992 * trw_layer_track_statistics:
4994 * Show track statistics.
4995 * ATM jump to the stats page in the properties
4996 * TODO: consider separating the stats into an individual dialog?
4998 static void trw_layer_track_statistics ( menu_array_sublayer values )
5000 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5002 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5003 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5005 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5007 if ( trk && trk->name ) {
5008 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5018 * Update the treeview of the track id - primarily to update the icon
5020 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5026 gpointer *trkf = NULL;
5027 if ( trk->is_route )
5028 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5030 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5032 if ( trkf && udata.uuid ) {
5034 GtkTreeIter *iter = NULL;
5035 if ( trk->is_route )
5036 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5038 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5041 // TODO: Make this a function
5042 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5043 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5044 ((trk->color.green & 0xff00) << 8) |
5045 (trk->color.blue & 0xff00);
5046 gdk_pixbuf_fill ( pixbuf, pixel );
5047 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5048 g_object_unref (pixbuf);
5055 Parameter 1 -> VikLayersPanel
5056 Parameter 2 -> VikLayer
5057 Parameter 3 -> VikViewport
5059 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5062 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
5063 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5066 /* since vlp not set, vl & vvp should be valid instead! */
5068 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
5069 vik_layer_emit_update ( VIK_LAYER(vl) );
5074 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5076 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5078 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5079 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5081 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5083 if ( track && track->trackpoints )
5084 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5087 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5089 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5091 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5092 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5094 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5096 if ( track && track->trackpoints )
5098 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5100 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5101 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5102 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5103 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5104 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5108 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5110 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5112 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5113 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5115 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5120 // Converting a track to a route can be a bit more complicated,
5121 // so give a chance to change our minds:
5122 if ( !trk->is_route &&
5123 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5124 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5126 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5127 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5132 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5135 trk_copy->is_route = !trk_copy->is_route;
5137 // ATM can't set name to self - so must create temporary copy
5138 gchar *name = g_strdup ( trk_copy->name );
5140 // Delete old one and then add new one
5141 if ( trk->is_route ) {
5142 vik_trw_layer_delete_route ( vtl, trk );
5143 vik_trw_layer_add_track ( vtl, name, trk_copy );
5146 // Extra route conversion bits...
5147 vik_track_merge_segments ( trk_copy );
5148 vik_track_to_routepoints ( trk_copy );
5150 vik_trw_layer_delete_track ( vtl, trk );
5151 vik_trw_layer_add_route ( vtl, name, trk_copy );
5155 // Update in case color of track / route changes when moving between sublayers
5156 vik_layer_emit_update ( VIK_LAYER(vtl) );
5159 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5161 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5163 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5164 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5166 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5169 vik_track_anonymize_times ( track );
5172 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5174 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5176 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5177 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5179 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5184 vtl->current_track = track;
5185 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);
5187 if ( track->trackpoints )
5188 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5192 * extend a track using route finder
5194 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5196 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5197 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5200 if ( !track->trackpoints )
5203 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5204 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5205 vtl->route_finder_current_track = track;
5206 vtl->route_finder_started = TRUE;
5208 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5214 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5216 // If have a vlp then perform a basic test to see if any DEM info available...
5218 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5220 if ( !g_list_length(dems) ) {
5221 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5229 * apply_dem_data_common:
5231 * A common function for applying the DEM values and reporting the results.
5233 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5235 if ( !trw_layer_dem_test ( vtl, vlp ) )
5238 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5239 // Inform user how much was changed
5241 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5242 g_snprintf(str, 64, tmp_str, changed);
5243 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5246 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5248 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5250 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5251 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5253 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5256 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5259 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5261 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5263 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5264 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5266 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5269 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5275 * A common function for applying the elevation smoothing and reporting the results.
5277 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5279 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5280 // Inform user how much was changed
5282 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5283 g_snprintf(str, 64, tmp_str, changed);
5284 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5290 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5292 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5294 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5295 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5297 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5302 smooth_it ( vtl, track, FALSE );
5305 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5307 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5309 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5310 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5312 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5317 smooth_it ( vtl, track, TRUE );
5321 * Commonal helper function
5323 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5326 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5327 g_snprintf(str, 64, tmp_str, changed);
5328 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5331 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5333 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5334 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5336 if ( !trw_layer_dem_test ( vtl, vlp ) )
5340 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5342 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5344 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5348 GHashTableIter iter;
5349 gpointer key, value;
5351 g_hash_table_iter_init ( &iter, vtl->waypoints );
5352 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5353 VikWaypoint *wp = VIK_WAYPOINT(value);
5354 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5357 wp_changed_message ( vtl, changed );
5360 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5362 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5363 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5365 if ( !trw_layer_dem_test ( vtl, vlp ) )
5369 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5371 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5373 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5377 GHashTableIter iter;
5378 gpointer key, value;
5380 g_hash_table_iter_init ( &iter, vtl->waypoints );
5381 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5382 VikWaypoint *wp = VIK_WAYPOINT(value);
5383 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5386 wp_changed_message ( vtl, changed );
5389 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5391 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5393 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5394 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5396 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5400 if ( !track->trackpoints )
5402 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5405 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5407 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5409 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5410 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5412 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5417 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5420 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5423 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5425 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5427 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5428 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5430 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5435 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5438 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5441 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5443 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5445 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5446 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5448 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5453 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5456 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5460 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5462 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5464 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5466 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5467 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5469 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5471 if ( trk && trk->trackpoints )
5473 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5474 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5475 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5476 if ( values[MA_VLP] )
5477 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5479 vik_layer_emit_update ( VIK_LAYER(vtl) );
5484 * Refine the selected track/route with a routing engine.
5485 * The routing engine is selected by the user, when requestiong the job.
5487 static void trw_layer_route_refine ( menu_array_sublayer values )
5489 static gint last_engine = 0;
5490 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5493 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5494 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5496 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5498 if ( trk && trk->trackpoints )
5500 /* Check size of the route */
5501 int nb = vik_track_get_tp_count(trk);
5503 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5504 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5505 GTK_MESSAGE_WARNING,
5506 GTK_BUTTONS_OK_CANCEL,
5507 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5509 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5510 gtk_widget_destroy ( dialog );
5511 if (response != GTK_RESPONSE_OK )
5514 /* Select engine from dialog */
5515 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5516 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5517 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5519 GTK_RESPONSE_REJECT,
5521 GTK_RESPONSE_ACCEPT,
5523 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5524 gtk_widget_show_all(label);
5526 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5528 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5529 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5530 gtk_widget_show_all(combo);
5532 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5534 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5536 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5538 /* Dialog validated: retrieve selected engine and do the job */
5539 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5540 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5543 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5545 /* Force saving track */
5546 /* FIXME: remove or rename this hack */
5547 vtl->route_finder_check_added_track = TRUE;
5550 vik_routing_engine_refine (routing, vtl, trk);
5552 /* FIXME: remove or rename this hack */
5553 if ( vtl->route_finder_added_track )
5554 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5556 vtl->route_finder_added_track = NULL;
5557 vtl->route_finder_check_added_track = FALSE;
5559 vik_layer_emit_update ( VIK_LAYER(vtl) );
5561 /* Restore cursor */
5562 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5564 gtk_widget_destroy ( dialog );
5568 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5570 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5571 trw_layer_tpwin_init ( vtl );
5574 /*************************************
5575 * merge/split by time routines
5576 *************************************/
5578 /* called for each key in track hash table.
5579 * If the current track has the same time stamp type, add it to the result,
5580 * except the one pointed by "exclude".
5581 * set exclude to NULL if there is no exclude to check.
5582 * Note that the result is in reverse (for performance reasons).
5587 gboolean with_timestamps;
5589 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5591 twt_udata *user_data = udata;
5592 VikTrackpoint *p1, *p2;
5593 VikTrack *trk = VIK_TRACK(value);
5594 if (trk == (VikTrack *)user_data->exclude) {
5598 if (trk->trackpoints) {
5599 p1 = vik_track_get_tp_first(trk);
5600 p2 = vik_track_get_tp_last(trk);
5602 if ( user_data->with_timestamps ) {
5603 if (!p1->has_timestamp || !p2->has_timestamp) {
5608 // Don't add tracks with timestamps when getting non timestamp tracks
5609 if (p1->has_timestamp || p2->has_timestamp) {
5615 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5618 /* called for each key in track hash table. if original track user_data[1] is close enough
5619 * to the passed one, add it to list in user_data[0]
5621 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5624 VikTrackpoint *p1, *p2;
5625 VikTrack *trk = VIK_TRACK(value);
5627 GList **nearby_tracks = ((gpointer *)user_data)[0];
5628 GList *tpoints = ((gpointer *)user_data)[1];
5631 * detect reasons for not merging, and return
5632 * if no reason is found not to merge, then do it.
5635 // Exclude the original track from the compiled list
5636 if (trk->trackpoints == tpoints) {
5640 t1 = vik_track_get_tp_first(trk)->timestamp;
5641 t2 = vik_track_get_tp_last(trk)->timestamp;
5643 if (trk->trackpoints) {
5644 p1 = vik_track_get_tp_first(trk);
5645 p2 = vik_track_get_tp_last(trk);
5647 if (!p1->has_timestamp || !p2->has_timestamp) {
5648 //g_print("no timestamp\n");
5652 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5653 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5654 if (! (abs(t1 - p2->timestamp) < threshold ||
5656 abs(p1->timestamp - t2) < threshold)
5663 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5666 /* comparison function used to sort tracks; a and b are hash table keys */
5667 /* Not actively used - can be restored if needed
5668 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5670 GHashTable *tracks = user_data;
5673 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5674 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5676 if (t1 < t2) return -1;
5677 if (t1 > t2) return 1;
5682 /* comparison function used to sort trackpoints */
5683 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5685 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5687 if (t1 < t2) return -1;
5688 if (t1 > t2) return 1;
5693 * comparison function which can be used to sort tracks or waypoints by name
5695 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5697 const gchar* namea = (const gchar*) a;
5698 const gchar* nameb = (const gchar*) b;
5699 if ( namea == NULL || nameb == NULL)
5702 // Same sort method as used in the vik_treeview_*_alphabetize functions
5703 return strcmp ( namea, nameb );
5707 * Attempt to merge selected track with other tracks specified by the user
5708 * Tracks to merge with must be of the same 'type' as the selected track -
5709 * either all with timestamps, or all without timestamps
5711 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5713 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5714 GList *other_tracks = NULL;
5715 GHashTable *ght_tracks;
5716 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5717 ght_tracks = vtl->routes;
5719 ght_tracks = vtl->tracks;
5721 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5726 if ( !track->trackpoints )
5730 udata.result = &other_tracks;
5731 udata.exclude = track->trackpoints;
5732 // Allow merging with 'similar' time type time tracks
5733 // i.e. either those times, or those without
5734 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5736 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5737 other_tracks = g_list_reverse(other_tracks);
5739 if ( !other_tracks ) {
5740 if ( udata.with_timestamps )
5741 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5743 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5747 // Sort alphabetically for user presentation
5748 // Convert into list of names for usage with dialog function
5749 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5750 GList *other_tracks_names = NULL;
5751 GList *iter = g_list_first ( other_tracks );
5753 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5754 iter = g_list_next ( iter );
5757 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5759 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5763 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5764 g_list_free(other_tracks);
5765 g_list_free(other_tracks_names);
5770 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5771 VikTrack *merge_track;
5772 if ( track->is_route )
5773 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5775 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5778 vik_track_steal_and_append_trackpoints ( track, merge_track );
5779 if ( track->is_route )
5780 vik_trw_layer_delete_route (vtl, merge_track);
5782 vik_trw_layer_delete_track (vtl, merge_track);
5783 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5786 for (l = merge_list; l != NULL; l = g_list_next(l))
5788 g_list_free(merge_list);
5790 vik_layer_emit_update( VIK_LAYER(vtl) );
5794 // c.f. trw_layer_sorted_track_id_by_name_list
5795 // but don't add the specified track to the list (normally current track)
5796 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5798 twt_udata *user_data = udata;
5801 if (trk->trackpoints == user_data->exclude) {
5805 // Sort named list alphabetically
5806 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5810 * Join - this allows combining 'tracks' and 'track routes'
5811 * i.e. doesn't care about whether tracks have consistent timestamps
5812 * ATM can only append one track at a time to the currently selected track
5814 static void trw_layer_append_track ( menu_array_sublayer values )
5817 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5819 GHashTable *ght_tracks;
5820 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5821 ght_tracks = vtl->routes;
5823 ght_tracks = vtl->tracks;
5825 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5830 GList *other_tracks_names = NULL;
5832 // Sort alphabetically for user presentation
5833 // Convert into list of names for usage with dialog function
5834 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5836 udata.result = &other_tracks_names;
5837 udata.exclude = trk->trackpoints;
5839 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5841 // Note the limit to selecting one track only
5842 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5843 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5844 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5847 trk->is_route ? _("Append Route"): _("Append Track"),
5848 trk->is_route ? _("Select the route to append after the current route") :
5849 _("Select the track to append after the current track") );
5851 g_list_free(other_tracks_names);
5853 // It's a list, but shouldn't contain more than one other track!
5854 if ( append_list ) {
5856 for (l = append_list; l != NULL; l = g_list_next(l)) {
5857 // TODO: at present this uses the first track found by name,
5858 // which with potential multiple same named tracks may not be the one selected...
5859 VikTrack *append_track;
5860 if ( trk->is_route )
5861 append_track = vik_trw_layer_get_route ( vtl, l->data );
5863 append_track = vik_trw_layer_get_track ( vtl, l->data );
5865 if ( append_track ) {
5866 vik_track_steal_and_append_trackpoints ( trk, append_track );
5867 if ( trk->is_route )
5868 vik_trw_layer_delete_route (vtl, append_track);
5870 vik_trw_layer_delete_track (vtl, append_track);
5873 for (l = append_list; l != NULL; l = g_list_next(l))
5875 g_list_free(append_list);
5877 vik_layer_emit_update( VIK_LAYER(vtl) );
5882 * Very similar to trw_layer_append_track for joining
5883 * but this allows selection from the 'other' list
5884 * If a track is selected, then is shows routes and joins the selected one
5885 * If a route is selected, then is shows tracks and joins the selected one
5887 static void trw_layer_append_other ( menu_array_sublayer values )
5890 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5892 GHashTable *ght_mykind, *ght_others;
5893 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5894 ght_mykind = vtl->routes;
5895 ght_others = vtl->tracks;
5898 ght_mykind = vtl->tracks;
5899 ght_others = vtl->routes;
5902 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5907 GList *other_tracks_names = NULL;
5909 // Sort alphabetically for user presentation
5910 // Convert into list of names for usage with dialog function
5911 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5913 udata.result = &other_tracks_names;
5914 udata.exclude = trk->trackpoints;
5916 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5918 // Note the limit to selecting one track only
5919 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5920 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5921 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5924 trk->is_route ? _("Append Track"): _("Append Route"),
5925 trk->is_route ? _("Select the track to append after the current route") :
5926 _("Select the route to append after the current track") );
5928 g_list_free(other_tracks_names);
5930 // It's a list, but shouldn't contain more than one other track!
5931 if ( append_list ) {
5933 for (l = append_list; l != NULL; l = g_list_next(l)) {
5934 // TODO: at present this uses the first track found by name,
5935 // which with potential multiple same named tracks may not be the one selected...
5937 // Get FROM THE OTHER TYPE list
5938 VikTrack *append_track;
5939 if ( trk->is_route )
5940 append_track = vik_trw_layer_get_track ( vtl, l->data );
5942 append_track = vik_trw_layer_get_route ( vtl, l->data );
5944 if ( append_track ) {
5946 if ( !append_track->is_route &&
5947 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5948 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5950 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5951 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5952 vik_track_merge_segments ( append_track );
5953 vik_track_to_routepoints ( append_track );
5960 vik_track_steal_and_append_trackpoints ( trk, append_track );
5962 // Delete copied which is FROM THE OTHER TYPE list
5963 if ( trk->is_route )
5964 vik_trw_layer_delete_track (vtl, append_track);
5966 vik_trw_layer_delete_route (vtl, append_track);
5969 for (l = append_list; l != NULL; l = g_list_next(l))
5971 g_list_free(append_list);
5972 vik_layer_emit_update( VIK_LAYER(vtl) );
5976 /* merge by segments */
5977 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
5979 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5980 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5981 guint segments = vik_track_merge_segments ( trk );
5982 // NB currently no need to redraw as segments not actually shown on the display
5983 // However inform the user of what happened:
5985 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5986 g_snprintf(str, 64, tmp_str, segments);
5987 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5990 /* merge by time routine */
5991 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
5993 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5997 GList *tracks_with_timestamp = NULL;
5998 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5999 if (orig_trk->trackpoints &&
6000 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6001 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6006 udata.result = &tracks_with_timestamp;
6007 udata.exclude = orig_trk->trackpoints;
6008 udata.with_timestamps = TRUE;
6009 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6010 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6012 if (!tracks_with_timestamp) {
6013 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6016 g_list_free(tracks_with_timestamp);
6018 static guint threshold_in_minutes = 1;
6019 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6020 _("Merge Threshold..."),
6021 _("Merge when time between tracks less than:"),
6022 &threshold_in_minutes)) {
6026 // keep attempting to merge all tracks until no merges within the time specified is possible
6027 gboolean attempt_merge = TRUE;
6028 GList *nearby_tracks = NULL;
6030 static gpointer params[3];
6032 while ( attempt_merge ) {
6034 // Don't try again unless tracks have changed
6035 attempt_merge = FALSE;
6037 trps = orig_trk->trackpoints;
6041 if (nearby_tracks) {
6042 g_list_free(nearby_tracks);
6043 nearby_tracks = NULL;
6046 params[0] = &nearby_tracks;
6047 params[1] = (gpointer)trps;
6048 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6050 /* get a list of adjacent-in-time tracks */
6051 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6054 GList *l = nearby_tracks;
6056 /* remove trackpoints from merged track, delete track */
6057 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6058 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6060 // Tracks have changed, therefore retry again against all the remaining tracks
6061 attempt_merge = TRUE;
6066 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6069 g_list_free(nearby_tracks);
6071 vik_layer_emit_update( VIK_LAYER(vtl) );
6075 * Split a track at the currently selected trackpoint
6077 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6079 if ( !vtl->current_tpl )
6082 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6083 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6085 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6086 GList *newglist = g_list_alloc ();
6087 newglist->prev = NULL;
6088 newglist->next = vtl->current_tpl->next;
6089 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6090 tr->trackpoints = newglist;
6092 vtl->current_tpl->next->prev = newglist; /* end old track here */
6093 vtl->current_tpl->next = NULL;
6095 // Bounds of the selected track changed due to the split
6096 vik_track_calculate_bounds ( vtl->current_tp_track );
6098 vtl->current_tpl = newglist; /* change tp to first of new track. */
6099 vtl->current_tp_track = tr;
6102 vik_trw_layer_add_route ( vtl, name, tr );
6104 vik_trw_layer_add_track ( vtl, name, tr );
6106 // Bounds of the new track created by the split
6107 vik_track_calculate_bounds ( tr );
6113 // Also need id of newly created track
6116 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6118 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6120 if ( trkf && udata.uuid )
6121 vtl->current_tp_id = udata.uuid;
6123 vtl->current_tp_id = NULL;
6125 vik_layer_emit_update(VIK_LAYER(vtl));
6131 /* split by time routine */
6132 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6134 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6135 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6136 GList *trps = track->trackpoints;
6138 GList *newlists = NULL;
6139 GList *newtps = NULL;
6140 static guint thr = 1;
6147 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6148 _("Split Threshold..."),
6149 _("Split when time between trackpoints exceeds:"),
6154 /* iterate through trackpoints, and copy them into new lists without touching original list */
6155 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6159 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6161 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6164 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6165 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6166 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6168 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6173 if (ts - prev_ts > thr*60) {
6174 /* flush accumulated trackpoints into new list */
6175 newlists = g_list_append(newlists, g_list_reverse(newtps));
6179 /* accumulate trackpoint copies in newtps, in reverse order */
6180 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6182 iter = g_list_next(iter);
6185 newlists = g_list_append(newlists, g_list_reverse(newtps));
6188 /* put lists of trackpoints into tracks */
6190 // Only bother updating if the split results in new tracks
6191 if (g_list_length (newlists) > 1) {
6196 tr = vik_track_copy ( track, FALSE );
6197 tr->trackpoints = (GList *)(iter->data);
6199 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6200 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6201 g_free ( new_tr_name );
6202 vik_track_calculate_bounds ( tr );
6203 iter = g_list_next(iter);
6205 // Remove original track and then update the display
6206 vik_trw_layer_delete_track (vtl, track);
6207 vik_layer_emit_update(VIK_LAYER(vtl));
6209 g_list_free(newlists);
6213 * Split a track by the number of points as specified by the user
6215 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6217 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6219 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6220 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6222 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6227 // Check valid track
6228 GList *trps = track->trackpoints;
6232 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6233 _("Split Every Nth Point"),
6234 _("Split on every Nth point:"),
6235 250, // Default value as per typical limited track capacity of various GPS devices
6239 // Was a valid number returned?
6245 GList *newlists = NULL;
6246 GList *newtps = NULL;
6251 /* accumulate trackpoint copies in newtps, in reverse order */
6252 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6254 if (count >= points) {
6255 /* flush accumulated trackpoints into new list */
6256 newlists = g_list_append(newlists, g_list_reverse(newtps));
6260 iter = g_list_next(iter);
6263 // If there is a remaining chunk put that into the new split list
6264 // This may well be the whole track if no split points were encountered
6266 newlists = g_list_append(newlists, g_list_reverse(newtps));
6269 /* put lists of trackpoints into tracks */
6271 // Only bother updating if the split results in new tracks
6272 if (g_list_length (newlists) > 1) {
6277 tr = vik_track_copy ( track, FALSE );
6278 tr->trackpoints = (GList *)(iter->data);
6280 if ( track->is_route ) {
6281 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6282 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6285 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6286 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6288 g_free ( new_tr_name );
6289 vik_track_calculate_bounds ( tr );
6291 iter = g_list_next(iter);
6293 // Remove original track and then update the display
6294 if ( track->is_route )
6295 vik_trw_layer_delete_route (vtl, track);
6297 vik_trw_layer_delete_track (vtl, track);
6298 vik_layer_emit_update(VIK_LAYER(vtl));
6300 g_list_free(newlists);
6304 * Split a track at the currently selected trackpoint
6306 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6308 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6309 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6310 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6314 * Split a track by its segments
6315 * Routes do not have segments so don't call this for routes
6317 static void trw_layer_split_segments ( menu_array_sublayer values )
6319 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6320 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6327 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6330 for ( i = 0; i < ntracks; i++ ) {
6332 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6333 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6334 g_free ( new_tr_name );
6339 // Remove original track
6340 vik_trw_layer_delete_track ( vtl, trk );
6341 vik_layer_emit_update ( VIK_LAYER(vtl) );
6344 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6347 /* end of split/merge routines */
6349 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6353 // Find available adjacent trackpoint
6354 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6355 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6356 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6358 // Delete current trackpoint
6359 vik_trackpoint_free ( vtl->current_tpl->data );
6360 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6362 // Set to current to the available adjacent trackpoint
6363 vtl->current_tpl = new_tpl;
6365 if ( vtl->current_tp_track ) {
6366 vik_track_calculate_bounds ( vtl->current_tp_track );
6370 // Delete current trackpoint
6371 vik_trackpoint_free ( vtl->current_tpl->data );
6372 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6373 trw_layer_cancel_current_tp ( vtl, FALSE );
6378 * Delete the selected point
6380 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6382 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6384 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6385 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6387 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6392 if ( !vtl->current_tpl )
6395 trw_layer_trackpoint_selected_delete ( vtl, trk );
6397 // Track has been updated so update tps:
6398 trw_layer_cancel_tps_of_track ( vtl, trk );
6400 vik_layer_emit_update ( VIK_LAYER(vtl) );
6404 * Delete adjacent track points at the same position
6405 * AKA Delete Dulplicates on the Properties Window
6407 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6409 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6411 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6412 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6414 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6419 gulong removed = vik_track_remove_dup_points ( trk );
6421 // Track has been updated so update tps:
6422 trw_layer_cancel_tps_of_track ( vtl, trk );
6424 // Inform user how much was deleted as it's not obvious from the normal view
6426 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6427 g_snprintf(str, 64, tmp_str, removed);
6428 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6430 vik_layer_emit_update ( VIK_LAYER(vtl) );
6434 * Delete adjacent track points with the same timestamp
6435 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6437 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6439 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6441 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6442 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6444 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6449 gulong removed = vik_track_remove_same_time_points ( trk );
6451 // Track has been updated so update tps:
6452 trw_layer_cancel_tps_of_track ( vtl, trk );
6454 // Inform user how much was deleted as it's not obvious from the normal view
6456 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6457 g_snprintf(str, 64, tmp_str, removed);
6458 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6460 vik_layer_emit_update ( VIK_LAYER(vtl) );
6466 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6468 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6470 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6471 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6473 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6478 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6480 vik_layer_emit_update ( VIK_LAYER(vtl) );
6483 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6485 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6487 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6488 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6490 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6495 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6497 vik_layer_emit_update ( VIK_LAYER(vtl) );
6503 static void trw_layer_reverse ( menu_array_sublayer values )
6505 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6507 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6508 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6510 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6515 vik_track_reverse ( track );
6517 vik_layer_emit_update ( VIK_LAYER(vtl) );
6521 * Similar to trw_layer_enum_item, but this uses a sorted method
6524 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6526 GList **list = (GList**)udata;
6527 // *list = g_list_prepend(*all, key); //unsorted method
6528 // Sort named list alphabetically
6529 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6534 * Now Waypoint specific sort
6536 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6538 GList **list = (GList**)udata;
6539 // Sort named list alphabetically
6540 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6544 * Track specific sort
6546 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6548 GList **list = (GList**)udata;
6549 // Sort named list alphabetically
6550 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6555 gboolean has_same_track_name;
6556 const gchar *same_track_name;
6557 } same_track_name_udata;
6559 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6561 const gchar* namea = (const gchar*) aa;
6562 const gchar* nameb = (const gchar*) bb;
6565 gint result = strcmp ( namea, nameb );
6567 if ( result == 0 ) {
6568 // Found two names the same
6569 same_track_name_udata *user_data = udata;
6570 user_data->has_same_track_name = TRUE;
6571 user_data->same_track_name = namea;
6574 // Leave ordering the same
6579 * Find out if any tracks have the same name in this hash table
6581 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6583 // Sort items by name, then compare if any next to each other are the same
6585 GList *track_names = NULL;
6586 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6589 if ( ! track_names )
6592 same_track_name_udata udata;
6593 udata.has_same_track_name = FALSE;
6595 // Use sort routine to traverse list comparing items
6596 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6597 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6598 // Still no tracks...
6602 return udata.has_same_track_name;
6606 * Force unqiue track names for the track table specified
6607 * Note the panel is a required parameter to enable the update of the names displayed
6608 * Specify if on tracks or else on routes
6610 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6612 // . Search list for an instance of repeated name
6613 // . get track of this name
6614 // . create new name
6615 // . rename track & update equiv. treeview iter
6616 // . repeat until all different
6618 same_track_name_udata udata;
6620 GList *track_names = NULL;
6621 udata.has_same_track_name = FALSE;
6622 udata.same_track_name = NULL;
6624 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6627 if ( ! track_names )
6630 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6632 // Still no tracks...
6633 if ( ! dummy_list1 )
6636 while ( udata.has_same_track_name ) {
6638 // Find a track with the same name
6641 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6643 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6647 g_critical("Houston, we've had a problem.");
6648 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6649 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6654 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6655 vik_track_set_name ( trk, newname );
6661 // Need want key of it for treeview update
6662 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6664 if ( trkf && udataU.uuid ) {
6668 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6670 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6673 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6675 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6677 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6681 // Start trying to find same names again...
6683 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6684 udata.has_same_track_name = FALSE;
6685 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6687 // No tracks any more - give up searching
6688 if ( ! dummy_list2 )
6689 udata.has_same_track_name = FALSE;
6693 vik_layers_panel_emit_update ( vlp );
6696 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6698 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6701 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6702 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6703 iter = &(vtl->tracks_iter);
6704 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6706 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6707 iter = &(vtl->routes_iter);
6708 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6710 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6711 iter = &(vtl->waypoints_iter);
6712 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6716 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6719 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6721 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6724 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6725 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6726 iter = &(vtl->tracks_iter);
6727 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6729 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6730 iter = &(vtl->routes_iter);
6731 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6733 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6734 iter = &(vtl->waypoints_iter);
6735 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6739 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6745 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6747 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6750 // Ensure list of track names offered is unique
6751 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6752 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6753 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6754 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6760 // Sort list alphabetically for better presentation
6761 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6764 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6768 // Get list of items to delete from the user
6769 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6772 _("Delete Selection"),
6773 _("Select tracks to delete"));
6776 // Delete requested tracks
6777 // since specificly requested, IMHO no need for extra confirmation
6778 if ( delete_list ) {
6780 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6781 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6782 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6784 g_list_free(delete_list);
6785 vik_layer_emit_update( VIK_LAYER(vtl) );
6792 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6794 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6797 // Ensure list of track names offered is unique
6798 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6799 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6800 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6801 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6807 // Sort list alphabetically for better presentation
6808 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6811 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6815 // Get list of items to delete from the user
6816 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6819 _("Delete Selection"),
6820 _("Select routes to delete") );
6823 // Delete requested routes
6824 // since specificly requested, IMHO no need for extra confirmation
6825 if ( delete_list ) {
6827 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6828 // This deletes first route it finds of that name (but uniqueness is enforced above)
6829 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6831 g_list_free(delete_list);
6832 vik_layer_emit_update( VIK_LAYER(vtl) );
6837 gboolean has_same_waypoint_name;
6838 const gchar *same_waypoint_name;
6839 } same_waypoint_name_udata;
6841 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6843 const gchar* namea = (const gchar*) aa;
6844 const gchar* nameb = (const gchar*) bb;
6847 gint result = strcmp ( namea, nameb );
6849 if ( result == 0 ) {
6850 // Found two names the same
6851 same_waypoint_name_udata *user_data = udata;
6852 user_data->has_same_waypoint_name = TRUE;
6853 user_data->same_waypoint_name = namea;
6856 // Leave ordering the same
6861 * Find out if any waypoints have the same name in this layer
6863 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6865 // Sort items by name, then compare if any next to each other are the same
6867 GList *waypoint_names = NULL;
6868 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6871 if ( ! waypoint_names )
6874 same_waypoint_name_udata udata;
6875 udata.has_same_waypoint_name = FALSE;
6877 // Use sort routine to traverse list comparing items
6878 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6879 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6880 // Still no waypoints...
6884 return udata.has_same_waypoint_name;
6888 * Force unqiue waypoint names for this layer
6889 * Note the panel is a required parameter to enable the update of the names displayed
6891 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6893 // . Search list for an instance of repeated name
6894 // . get waypoint of this name
6895 // . create new name
6896 // . rename waypoint & update equiv. treeview iter
6897 // . repeat until all different
6899 same_waypoint_name_udata udata;
6901 GList *waypoint_names = NULL;
6902 udata.has_same_waypoint_name = FALSE;
6903 udata.same_waypoint_name = NULL;
6905 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6908 if ( ! waypoint_names )
6911 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6913 // Still no waypoints...
6914 if ( ! dummy_list1 )
6917 while ( udata.has_same_waypoint_name ) {
6919 // Find a waypoint with the same name
6920 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6924 g_critical("Houston, we've had a problem.");
6925 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6926 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6931 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6933 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6935 // Start trying to find same names again...
6936 waypoint_names = NULL;
6937 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6938 udata.has_same_waypoint_name = FALSE;
6939 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6941 // No waypoints any more - give up searching
6942 if ( ! dummy_list2 )
6943 udata.has_same_waypoint_name = FALSE;
6947 vik_layers_panel_emit_update ( vlp );
6953 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
6955 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6958 // Ensure list of waypoint names offered is unique
6959 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6960 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6961 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6962 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
6968 // Sort list alphabetically for better presentation
6969 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6971 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6975 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6977 // Get list of items to delete from the user
6978 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6981 _("Delete Selection"),
6982 _("Select waypoints to delete"));
6985 // Delete requested waypoints
6986 // since specificly requested, IMHO no need for extra confirmation
6987 if ( delete_list ) {
6989 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6990 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6991 trw_layer_delete_waypoint_by_name (vtl, l->data);
6993 g_list_free(delete_list);
6995 trw_layer_calculate_bounds_waypoints ( vtl );
6996 vik_layer_emit_update( VIK_LAYER(vtl) );
7004 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7006 vik_treeview_item_toggle_visible ( vt, it );
7012 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7014 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7020 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7022 wp->visible = GPOINTER_TO_INT (on_off);
7028 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7030 wp->visible = !wp->visible;
7036 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7038 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7039 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7040 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7041 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7043 vik_layer_emit_update ( VIK_LAYER(vtl) );
7049 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7051 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7052 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7053 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7054 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7056 vik_layer_emit_update ( VIK_LAYER(vtl) );
7062 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7064 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7065 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7066 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7068 vik_layer_emit_update ( VIK_LAYER(vtl) );
7074 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7076 trk->visible = GPOINTER_TO_INT (on_off);
7082 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7084 trk->visible = !trk->visible;
7090 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7092 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7093 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7094 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7095 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7097 vik_layer_emit_update ( VIK_LAYER(vtl) );
7103 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7105 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7106 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7107 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7108 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7110 vik_layer_emit_update ( VIK_LAYER(vtl) );
7116 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7118 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7119 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7120 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7122 vik_layer_emit_update ( VIK_LAYER(vtl) );
7128 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7130 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7131 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7132 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7133 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7135 vik_layer_emit_update ( VIK_LAYER(vtl) );
7141 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7143 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7144 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7145 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7146 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7148 vik_layer_emit_update ( VIK_LAYER(vtl) );
7154 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7156 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7157 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7158 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7160 vik_layer_emit_update ( VIK_LAYER(vtl) );
7164 * vik_trw_layer_build_waypoint_list_t:
7166 * Helper function to construct a list of #vik_trw_waypoint_list_t
7168 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7170 GList *waypoints_and_layers = NULL;
7171 // build waypoints_and_layers list
7172 while ( waypoints ) {
7173 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7174 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7176 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7177 waypoints = g_list_next ( waypoints );
7179 return waypoints_and_layers;
7183 * trw_layer_create_waypoint_list:
7185 * Create the latest list of waypoints with the associated layer(s)
7186 * Although this will always be from a single layer here
7188 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7190 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7191 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7193 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7197 * trw_layer_analyse_close:
7199 * Stuff to do on dialog closure
7201 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7203 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7204 gtk_widget_destroy ( dialog );
7205 vtl->tracks_analysis_dialog = NULL;
7209 * vik_trw_layer_build_track_list_t:
7211 * Helper function to construct a list of #vik_trw_track_list_t
7213 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7215 GList *tracks_and_layers = NULL;
7216 // build tracks_and_layers list
7218 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7219 vtdl->trk = VIK_TRACK(tracks->data);
7221 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7222 tracks = g_list_next ( tracks );
7224 return tracks_and_layers;
7228 * trw_layer_create_track_list:
7230 * Create the latest list of tracks with the associated layer(s)
7231 * Although this will always be from a single layer here
7233 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7235 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7236 GList *tracks = NULL;
7237 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7238 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7240 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7242 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7245 static void trw_layer_tracks_stats ( menu_array_layer values )
7247 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7248 // There can only be one!
7249 if ( vtl->tracks_analysis_dialog )
7252 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7253 VIK_LAYER(vtl)->name,
7255 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7256 trw_layer_create_track_list,
7257 trw_layer_analyse_close );
7263 static void trw_layer_routes_stats ( menu_array_layer values )
7265 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7266 // There can only be one!
7267 if ( vtl->tracks_analysis_dialog )
7270 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7271 VIK_LAYER(vtl)->name,
7273 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7274 trw_layer_create_track_list,
7275 trw_layer_analyse_close );
7278 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7280 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7281 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7283 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7286 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7288 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7289 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7292 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7293 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7297 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7299 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7300 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7303 if ( !strncmp(wp->comment, "http", 4) ) {
7304 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7305 } else if ( !strncmp(wp->description, "http", 4) ) {
7306 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7310 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7312 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7314 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7316 // No actual change to the name supplied
7318 if (strcmp(newname, wp->name) == 0 )
7321 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7324 // An existing waypoint has been found with the requested name
7325 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7326 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7331 // Update WP name and refresh the treeview
7332 vik_waypoint_set_name (wp, newname);
7334 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7335 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7337 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7342 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7344 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7346 // No actual change to the name supplied
7348 if (strcmp(newname, trk->name) == 0)
7351 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7354 // An existing track has been found with the requested name
7355 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7356 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7360 // Update track name and refresh GUI parts
7361 vik_track_set_name (trk, newname);
7363 // Update any subwindows that could be displaying this track which has changed name
7364 // Only one Track Edit Window
7365 if ( l->current_tp_track == trk && l->tpwin ) {
7366 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7368 // Property Dialog of the track
7369 vik_trw_layer_propwin_update ( trk );
7371 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7372 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7374 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7379 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7381 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7383 // No actual change to the name supplied
7385 if (strcmp(newname, trk->name) == 0)
7388 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7391 // An existing track has been found with the requested name
7392 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7393 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7397 // Update track name and refresh GUI parts
7398 vik_track_set_name (trk, newname);
7400 // Update any subwindows that could be displaying this track which has changed name
7401 // Only one Track Edit Window
7402 if ( l->current_tp_track == trk && l->tpwin ) {
7403 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7405 // Property Dialog of the track
7406 vik_trw_layer_propwin_update ( trk );
7408 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7409 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7411 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7418 static gboolean is_valid_geocache_name ( gchar *str )
7420 gint len = strlen ( str );
7421 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]));
7424 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7426 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7427 a_acquire_set_filter_track ( trk );
7430 #ifdef VIK_CONFIG_GOOGLE
7431 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7433 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7434 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7437 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7439 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7441 gchar *escaped = uri_escape ( tr->comment );
7442 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7443 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7450 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7451 /* viewpoint is now available instead */
7452 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7454 static menu_array_sublayer pass_along;
7456 gboolean rv = FALSE;
7458 pass_along[MA_VTL] = l;
7459 pass_along[MA_VLP] = vlp;
7460 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7461 pass_along[MA_SUBLAYER_ID] = sublayer;
7462 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7463 pass_along[MA_VVP] = vvp;
7464 pass_along[MA_TV_ITER] = iter;
7465 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7467 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7471 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7473 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7474 gtk_widget_show ( item );
7476 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7477 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7478 if (tr && tr->property_dialog)
7479 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7481 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7482 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7483 if (tr && tr->property_dialog)
7484 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7487 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7490 gtk_widget_show ( item );
7492 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7493 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7494 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7495 gtk_widget_show ( item );
7497 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7499 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7500 gtk_widget_show ( item );
7502 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7504 // Always create separator as now there is always at least the transform menu option
7505 item = gtk_menu_item_new ();
7506 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7507 gtk_widget_show ( item );
7509 /* could be a right-click using the tool */
7510 if ( vlp != NULL ) {
7511 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7512 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7513 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7514 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7515 gtk_widget_show ( item );
7518 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7520 if ( wp && wp->name ) {
7521 if ( is_valid_geocache_name ( wp->name ) ) {
7522 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7524 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7525 gtk_widget_show ( item );
7527 #ifdef VIK_CONFIG_GEOTAG
7528 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7530 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7531 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7532 gtk_widget_show ( item );
7536 if ( wp && wp->image )
7538 // Set up image paramater
7539 pass_along[MA_MISC] = wp->image;
7541 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7542 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
7543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7544 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7545 gtk_widget_show ( item );
7547 #ifdef VIK_CONFIG_GEOTAG
7548 GtkWidget *geotag_submenu = gtk_menu_new ();
7549 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7550 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7551 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7552 gtk_widget_show ( item );
7553 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7555 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7556 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7557 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7558 gtk_widget_show ( item );
7560 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7562 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7563 gtk_widget_show ( item );
7569 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7570 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7571 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7572 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7574 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7575 gtk_widget_show ( item );
7581 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7582 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7585 gtk_widget_show ( item );
7586 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7587 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7588 gtk_widget_set_sensitive ( item, TRUE );
7590 gtk_widget_set_sensitive ( item, FALSE );
7593 item = gtk_menu_item_new ();
7594 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7595 gtk_widget_show ( item );
7598 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7601 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7602 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7603 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7604 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7605 gtk_widget_show ( item );
7608 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7610 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7613 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7614 gtk_widget_show ( item );
7616 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7617 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7619 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7620 gtk_widget_show ( item );
7622 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7623 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7625 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7626 gtk_widget_show ( item );
7628 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7629 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7630 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7631 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7632 gtk_widget_show ( item );
7634 GtkWidget *vis_submenu = gtk_menu_new ();
7635 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7636 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7637 gtk_widget_show ( item );
7638 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7640 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7643 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7644 gtk_widget_show ( item );
7646 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7647 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7648 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7649 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7650 gtk_widget_show ( item );
7652 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7653 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7656 gtk_widget_show ( item );
7658 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7659 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7664 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7668 if ( l->current_track && !l->current_track->is_route ) {
7669 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7670 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7671 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7672 gtk_widget_show ( item );
7674 item = gtk_menu_item_new ();
7675 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7676 gtk_widget_show ( item );
7679 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7680 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7681 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7682 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7683 gtk_widget_show ( item );
7685 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7686 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7687 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7688 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7689 gtk_widget_show ( item );
7690 // Make it available only when a new track *not* already in progress
7691 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7693 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7694 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7695 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7696 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7697 gtk_widget_show ( item );
7699 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7700 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7701 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7702 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7703 gtk_widget_show ( item );
7705 GtkWidget *vis_submenu = gtk_menu_new ();
7706 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7707 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7708 gtk_widget_show ( item );
7709 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7711 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7712 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7713 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7714 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7715 gtk_widget_show ( item );
7717 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7719 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7720 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7721 gtk_widget_show ( item );
7723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7728 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7729 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7730 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7731 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7732 gtk_widget_show ( item );
7734 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7736 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7737 gtk_widget_show ( item );
7740 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7744 if ( l->current_track && l->current_track->is_route ) {
7745 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7746 // Reuse finish track method
7747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7748 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7749 gtk_widget_show ( item );
7751 item = gtk_menu_item_new ();
7752 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7753 gtk_widget_show ( item );
7756 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7757 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7759 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7760 gtk_widget_show ( item );
7762 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7763 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7764 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7765 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7766 gtk_widget_show ( item );
7767 // Make it available only when a new track *not* already in progress
7768 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7770 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7771 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7772 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7773 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7774 gtk_widget_show ( item );
7776 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7777 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7779 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7780 gtk_widget_show ( item );
7782 GtkWidget *vis_submenu = gtk_menu_new ();
7783 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7784 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7785 gtk_widget_show ( item );
7786 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7788 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7789 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7790 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7791 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7792 gtk_widget_show ( item );
7794 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7797 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7798 gtk_widget_show ( item );
7800 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7801 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7802 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7803 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7805 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7806 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7807 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7808 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7810 gtk_widget_show ( item );
7812 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7814 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7815 gtk_widget_show ( item );
7819 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7820 GtkWidget *submenu_sort = gtk_menu_new ();
7821 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7822 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7823 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7824 gtk_widget_show ( item );
7825 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7827 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7828 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7830 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7831 gtk_widget_show ( item );
7833 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7834 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7836 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7837 gtk_widget_show ( item );
7840 GtkWidget *upload_submenu = gtk_menu_new ();
7842 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7844 item = gtk_menu_item_new ();
7845 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7846 gtk_widget_show ( item );
7848 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7849 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7850 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7851 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7852 if ( l->current_track ) {
7853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7854 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7855 gtk_widget_show ( item );
7858 item = gtk_menu_item_new ();
7859 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7860 gtk_widget_show ( item );
7863 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7864 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7866 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7867 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7868 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7869 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7870 gtk_widget_show ( item );
7872 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7874 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7875 gtk_widget_show ( item );
7877 GtkWidget *goto_submenu;
7878 goto_submenu = gtk_menu_new ();
7879 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7880 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7881 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7882 gtk_widget_show ( item );
7883 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7885 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7888 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7889 gtk_widget_show ( item );
7891 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7892 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7894 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7895 gtk_widget_show ( item );
7897 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7898 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7899 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7900 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7901 gtk_widget_show ( item );
7903 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7904 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7905 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7906 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7907 gtk_widget_show ( item );
7909 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7910 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7911 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7912 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7913 gtk_widget_show ( item );
7915 // Routes don't have speeds
7916 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7917 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7918 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7920 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7921 gtk_widget_show ( item );
7924 GtkWidget *combine_submenu;
7925 combine_submenu = gtk_menu_new ();
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7927 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, 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), combine_submenu );
7932 // Routes don't have times or segments...
7933 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7934 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7935 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7936 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7937 gtk_widget_show ( item );
7939 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7940 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7941 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7942 gtk_widget_show ( item );
7945 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7948 gtk_widget_show ( item );
7950 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7951 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7953 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7954 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7955 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7956 gtk_widget_show ( item );
7958 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7959 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7961 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7963 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7964 gtk_widget_show ( item );
7966 GtkWidget *split_submenu;
7967 split_submenu = gtk_menu_new ();
7968 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7969 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7970 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7971 gtk_widget_show ( item );
7972 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7974 // Routes don't have times or segments...
7975 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7976 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7977 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7978 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7979 gtk_widget_show ( item );
7981 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7982 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7984 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7985 gtk_widget_show ( item );
7988 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7990 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7991 gtk_widget_show ( item );
7993 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7995 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7996 gtk_widget_show ( item );
7997 // Make it available only when a trackpoint is selected.
7998 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8000 GtkWidget *insert_submenu = gtk_menu_new ();
8001 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8002 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8003 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8004 gtk_widget_show ( item );
8005 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8007 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8009 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8010 gtk_widget_show ( item );
8011 // Make it available only when a point is selected
8012 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8014 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8016 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8017 gtk_widget_show ( item );
8018 // Make it available only when a point is selected
8019 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8021 GtkWidget *delete_submenu;
8022 delete_submenu = gtk_menu_new ();
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8024 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8025 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8026 gtk_widget_show ( item );
8027 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8029 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8030 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8032 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8033 gtk_widget_show ( item );
8034 // Make it available only when a point is selected
8035 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8037 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8039 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8040 gtk_widget_show ( item );
8042 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8044 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8045 gtk_widget_show ( item );
8047 GtkWidget *transform_submenu;
8048 transform_submenu = gtk_menu_new ();
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8051 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8052 gtk_widget_show ( item );
8053 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8055 GtkWidget *dem_submenu;
8056 dem_submenu = gtk_menu_new ();
8057 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8058 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
8059 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8060 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8062 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8064 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8065 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8066 gtk_widget_show ( item );
8068 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8069 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8070 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8071 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8072 gtk_widget_show ( item );
8074 GtkWidget *smooth_submenu;
8075 smooth_submenu = gtk_menu_new ();
8076 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8077 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8078 gtk_widget_show ( item );
8079 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8081 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8083 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8084 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8085 gtk_widget_show ( item );
8087 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8089 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8090 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8091 gtk_widget_show ( item );
8093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8094 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8096 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8099 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8100 gtk_widget_show ( item );
8102 // Routes don't have timestamps - so this is only available for tracks
8103 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8104 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8106 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8107 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8108 gtk_widget_show ( item );
8111 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8112 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8114 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8115 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8116 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8117 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8118 gtk_widget_show ( item );
8120 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8121 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8124 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8125 gtk_widget_show ( item );
8128 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8130 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8131 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8133 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8134 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
8135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8136 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8137 gtk_widget_show ( item );
8140 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8141 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8143 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8144 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8145 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8146 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8147 gtk_widget_show ( item );
8149 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8150 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8152 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8153 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8155 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8156 gtk_widget_show ( item );
8158 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8159 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8160 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
8161 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8162 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8163 gtk_widget_show ( item );
8166 // ATM can't upload a single waypoint but can do waypoints to a GPS
8167 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8168 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8171 gtk_widget_show ( item );
8172 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8174 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8177 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8178 gtk_widget_show ( item );
8182 #ifdef VIK_CONFIG_GOOGLE
8183 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8185 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8186 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8187 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8188 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8189 gtk_widget_show ( item );
8193 // Some things aren't usable with routes
8194 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8195 #ifdef VIK_CONFIG_OPENSTREETMAP
8196 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8197 // Convert internal pointer into track
8198 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8199 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8201 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8202 gtk_widget_show ( item );
8205 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8208 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8209 gtk_widget_show ( item );
8211 /* ATM This function is only available via the layers panel, due to needing a vlp */
8213 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8214 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8215 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8217 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8218 gtk_widget_show ( item );
8222 #ifdef VIK_CONFIG_GEOTAG
8223 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8225 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8226 gtk_widget_show ( item );
8230 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8231 // Only show on viewport popmenu when a trackpoint is selected
8232 if ( ! vlp && l->current_tpl ) {
8234 item = gtk_menu_item_new ();
8235 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8236 gtk_widget_show ( item );
8238 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8239 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8240 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8241 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8242 gtk_widget_show ( item );
8246 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8247 GtkWidget *transform_submenu;
8248 transform_submenu = gtk_menu_new ();
8249 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8250 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8251 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8252 gtk_widget_show ( item );
8253 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8255 GtkWidget *dem_submenu;
8256 dem_submenu = gtk_menu_new ();
8257 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8258 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
8259 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8260 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8262 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8264 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8265 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8266 gtk_widget_show ( item );
8268 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8269 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8270 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8271 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8272 gtk_widget_show ( item );
8275 gtk_widget_show_all ( GTK_WIDGET(menu) );
8280 // TODO: Probably better to rework this track manipulation in viktrack.c
8281 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8284 if (!vtl->current_tpl)
8287 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8288 VikTrackpoint *tp_other = NULL;
8291 if (!vtl->current_tpl->prev)
8293 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8295 if (!vtl->current_tpl->next)
8297 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8300 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8303 VikTrackpoint *tp_new = vik_trackpoint_new();
8304 struct LatLon ll_current, ll_other;
8305 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8306 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8308 /* main positional interpolation */
8309 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8310 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8312 /* Now other properties that can be interpolated */
8313 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8315 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8316 /* Note here the division is applied to each part, then added
8317 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8318 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8319 tp_new->has_timestamp = TRUE;
8322 if (tp_current->speed != NAN && tp_other->speed != NAN)
8323 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8325 /* TODO - improve interpolation of course, as it may not be correct.
8326 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8327 [similar applies if value is in radians] */
8328 if (tp_current->course != NAN && tp_other->course != NAN)
8329 tp_new->course = (tp_current->course + tp_other->course)/2;
8331 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8333 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8334 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8336 // Otherwise try routes
8337 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8341 gint index = g_list_index ( trk->trackpoints, tp_current );
8345 // NB no recalculation of bounds since it is inserted between points
8346 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8351 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8357 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8361 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8363 if ( vtl->current_tpl )
8365 vtl->current_tpl = NULL;
8366 vtl->current_tp_track = NULL;
8367 vtl->current_tp_id = NULL;
8368 vik_layer_emit_update(VIK_LAYER(vtl));
8372 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8374 g_assert ( vtl->tpwin != NULL );
8375 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8376 trw_layer_cancel_current_tp ( vtl, TRUE );
8378 if ( vtl->current_tpl == NULL )
8381 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8383 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8384 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8386 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8388 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8390 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8394 trw_layer_trackpoint_selected_delete ( vtl, tr );
8396 if ( vtl->current_tpl )
8397 // Reset dialog with the available adjacent trackpoint
8398 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8400 vik_layer_emit_update(VIK_LAYER(vtl));
8402 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8404 if ( vtl->current_tp_track )
8405 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8406 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8408 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8410 if ( vtl->current_tp_track )
8411 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8412 vik_layer_emit_update(VIK_LAYER(vtl));
8414 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8416 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8417 vik_layer_emit_update(VIK_LAYER(vtl));
8419 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8420 vik_layer_emit_update(VIK_LAYER(vtl));
8424 * trw_layer_dialog_shift:
8425 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8427 * Try to reposition a dialog if it's over the specified coord
8428 * so to not obscure the item of interest
8430 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8432 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8434 // Attempt force dialog to be shown so we can find out where it is more reliably...
8435 while ( gtk_events_pending() )
8436 gtk_main_iteration ();
8438 // get parent window position & size
8439 gint win_pos_x, win_pos_y;
8440 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8442 gint win_size_x, win_size_y;
8443 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8445 // get own dialog size
8446 gint dia_size_x, dia_size_y;
8447 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8449 // get own dialog position
8450 gint dia_pos_x, dia_pos_y;
8451 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8453 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8454 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8456 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8458 gint vp_xx, vp_yy; // In viewport pixels
8459 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8461 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8465 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8467 // Transform Viewport pixels into absolute pixels
8468 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8469 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8471 // Is dialog over the point (to within an ^^ edge value)
8472 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8473 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8477 gint hh = vik_viewport_get_height ( vvp );
8479 // Consider the difference in viewport to the full window
8480 gint offset_y = dest_y;
8481 // Add difference between dialog and window sizes
8482 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8484 if ( vp_yy > hh/2 ) {
8485 // Point in bottom half, move window to top half
8486 gtk_window_move ( dialog, dia_pos_x, offset_y );
8489 // Point in top half, move dialog down
8490 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8494 // Shift left<->right
8495 gint ww = vik_viewport_get_width ( vvp );
8497 // Consider the difference in viewport to the full window
8498 gint offset_x = dest_x;
8499 // Add difference between dialog and window sizes
8500 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8502 if ( vp_xx > ww/2 ) {
8503 // Point on right, move window to left
8504 gtk_window_move ( dialog, offset_x, dia_pos_y );
8507 // Point on left, move right
8508 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8516 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8520 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8521 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8522 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8523 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8525 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8527 if ( vtl->current_tpl ) {
8528 // get tp pixel position
8529 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8531 // Shift up<->down to try not to obscure the trackpoint.
8532 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8536 if ( vtl->current_tpl )
8537 if ( vtl->current_tp_track )
8538 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8539 /* set layer name and TP data */
8542 /***************************************************************************
8544 ***************************************************************************/
8546 /*** Utility data structures and functions ****/
8550 gint closest_x, closest_y;
8551 gboolean draw_images;
8552 gpointer *closest_wp_id;
8553 VikWaypoint *closest_wp;
8559 gint closest_x, closest_y;
8560 gpointer closest_track_id;
8561 VikTrackpoint *closest_tp;
8567 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8573 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8575 // If waypoint has an image then use the image size to select
8576 if ( params->draw_images && wp->image ) {
8577 gint slackx, slacky;
8578 slackx = wp->image_width / 2;
8579 slacky = wp->image_height / 2;
8581 if ( x <= params->x + slackx && x >= params->x - slackx
8582 && y <= params->y + slacky && y >= params->y - slacky ) {
8583 params->closest_wp_id = id;
8584 params->closest_wp = wp;
8585 params->closest_x = x;
8586 params->closest_y = y;
8589 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8590 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8591 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8593 params->closest_wp_id = id;
8594 params->closest_wp = wp;
8595 params->closest_x = x;
8596 params->closest_y = y;
8600 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8602 GList *tpl = t->trackpoints;
8608 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8614 tp = VIK_TRACKPOINT(tpl->data);
8616 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8618 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8619 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8620 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8622 params->closest_track_id = id;
8623 params->closest_tp = tp;
8624 params->closest_tpl = tpl;
8625 params->closest_x = x;
8626 params->closest_y = y;
8632 // ATM: Leave this as 'Track' only.
8633 // Not overly bothered about having a snap to route trackpoint capability
8634 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8636 TPSearchParams params;
8640 params.closest_track_id = NULL;
8641 params.closest_tp = NULL;
8642 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8643 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8644 return params.closest_tp;
8647 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8649 WPSearchParams params;
8653 params.draw_images = vtl->drawimages;
8654 params.closest_wp = NULL;
8655 params.closest_wp_id = NULL;
8656 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8657 return params.closest_wp;
8661 // Some forward declarations
8662 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8663 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8664 static void marker_end_move ( tool_ed_t *t );
8667 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8671 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8673 // Here always allow snapping back to the original location
8674 // this is useful when one decides not to move the thing afterall
8675 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8678 if ( event->state & GDK_CONTROL_MASK )
8680 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8682 new_coord = tp->coord;
8686 if ( event->state & GDK_SHIFT_MASK )
8688 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8690 new_coord = wp->coord;
8694 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8696 marker_moveto ( t, x, y );
8703 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8705 if ( t->holding && event->button == 1 )
8708 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8711 if ( event->state & GDK_CONTROL_MASK )
8713 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8715 new_coord = tp->coord;
8719 if ( event->state & GDK_SHIFT_MASK )
8721 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8723 new_coord = wp->coord;
8726 marker_end_move ( t );
8728 // Determine if working on a waypoint or a trackpoint
8729 if ( t->is_waypoint ) {
8730 // Update waypoint position
8731 vtl->current_wp->coord = new_coord;
8732 trw_layer_calculate_bounds_waypoints ( vtl );
8733 // Reset waypoint pointer
8734 vtl->current_wp = NULL;
8735 vtl->current_wp_id = NULL;
8738 if ( vtl->current_tpl ) {
8739 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8741 if ( vtl->current_tp_track )
8742 vik_track_calculate_bounds ( vtl->current_tp_track );
8745 if ( vtl->current_tp_track )
8746 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8747 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8751 vik_layer_emit_update ( VIK_LAYER(vtl) );
8758 Returns true if a waypoint or track is found near the requested event position for this particular layer
8759 The item found is automatically selected
8760 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8762 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8764 if ( event->button != 1 )
8767 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8770 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8774 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8776 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8778 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8779 WPSearchParams wp_params;
8780 wp_params.vvp = vvp;
8781 wp_params.x = event->x;
8782 wp_params.y = event->y;
8783 wp_params.draw_images = vtl->drawimages;
8784 wp_params.closest_wp_id = NULL;
8785 wp_params.closest_wp = NULL;
8787 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8789 if ( wp_params.closest_wp ) {
8792 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8794 // Too easy to move it so must be holding shift to start immediately moving it
8795 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8796 if ( event->state & GDK_SHIFT_MASK ||
8797 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8798 // Put into 'move buffer'
8799 // NB vvp & vw already set in tet
8800 tet->vtl = (gpointer)vtl;
8801 tet->is_waypoint = TRUE;
8803 marker_begin_move (tet, event->x, event->y);
8806 vtl->current_wp = wp_params.closest_wp;
8807 vtl->current_wp_id = wp_params.closest_wp_id;
8809 if ( event->type == GDK_2BUTTON_PRESS ) {
8810 if ( vtl->current_wp->image ) {
8811 menu_array_sublayer values;
8812 values[MA_VTL] = vtl;
8813 values[MA_MISC] = vtl->current_wp->image;
8814 trw_layer_show_picture ( values );
8818 vik_layer_emit_update ( VIK_LAYER(vtl) );
8824 // Used for both track and route lists
8825 TPSearchParams tp_params;
8826 tp_params.vvp = vvp;
8827 tp_params.x = event->x;
8828 tp_params.y = event->y;
8829 tp_params.closest_track_id = NULL;
8830 tp_params.closest_tp = NULL;
8831 tp_params.closest_tpl = NULL;
8832 tp_params.bbox = bbox;
8834 if (vtl->tracks_visible) {
8835 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8837 if ( tp_params.closest_tp ) {
8839 // Always select + highlight the track
8840 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8842 tet->is_waypoint = FALSE;
8844 // Select the Trackpoint
8845 // Can move it immediately when control held or it's the previously selected tp
8846 if ( event->state & GDK_CONTROL_MASK ||
8847 vtl->current_tpl == tp_params.closest_tpl ) {
8848 // Put into 'move buffer'
8849 // NB vvp & vw already set in tet
8850 tet->vtl = (gpointer)vtl;
8851 marker_begin_move (tet, event->x, event->y);
8854 vtl->current_tpl = tp_params.closest_tpl;
8855 vtl->current_tp_id = tp_params.closest_track_id;
8856 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8858 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8861 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8863 vik_layer_emit_update ( VIK_LAYER(vtl) );
8868 // Try again for routes
8869 if (vtl->routes_visible) {
8870 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8872 if ( tp_params.closest_tp ) {
8874 // Always select + highlight the track
8875 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8877 tet->is_waypoint = FALSE;
8879 // Select the Trackpoint
8880 // Can move it immediately when control held or it's the previously selected tp
8881 if ( event->state & GDK_CONTROL_MASK ||
8882 vtl->current_tpl == tp_params.closest_tpl ) {
8883 // Put into 'move buffer'
8884 // NB vvp & vw already set in tet
8885 tet->vtl = (gpointer)vtl;
8886 marker_begin_move (tet, event->x, event->y);
8889 vtl->current_tpl = tp_params.closest_tpl;
8890 vtl->current_tp_id = tp_params.closest_track_id;
8891 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8893 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8896 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8898 vik_layer_emit_update ( VIK_LAYER(vtl) );
8903 /* these aren't the droids you're looking for */
8904 vtl->current_wp = NULL;
8905 vtl->current_wp_id = NULL;
8906 trw_layer_cancel_current_tp ( vtl, FALSE );
8909 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8914 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8916 if ( event->button != 3 )
8919 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8922 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8925 /* Post menu for the currently selected item */
8927 /* See if a track is selected */
8928 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8929 if ( track && track->visible ) {
8931 if ( track->name ) {
8933 if ( vtl->track_right_click_menu )
8934 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8936 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8943 if ( track->is_route )
8944 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8946 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8948 if ( trkf && udataU.uuid ) {
8951 if ( track->is_route )
8952 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8954 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8956 trw_layer_sublayer_add_menu_items ( vtl,
8957 vtl->track_right_click_menu,
8959 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8965 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8971 /* See if a waypoint is selected */
8972 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8973 if ( waypoint && waypoint->visible ) {
8974 if ( waypoint->name ) {
8976 if ( vtl->wp_right_click_menu )
8977 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8979 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8982 udata.wp = waypoint;
8985 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8987 if ( wpf && udata.uuid ) {
8988 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8990 trw_layer_sublayer_add_menu_items ( vtl,
8991 vtl->wp_right_click_menu,
8993 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8998 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9007 /* background drawing hook, to be passed the viewport */
9008 static gboolean tool_sync_done = TRUE;
9010 static gboolean tool_sync(gpointer data)
9012 VikViewport *vvp = data;
9013 gdk_threads_enter();
9014 vik_viewport_sync(vvp);
9015 tool_sync_done = TRUE;
9016 gdk_threads_leave();
9020 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9023 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9024 gdk_gc_set_function ( t->gc, GDK_INVERT );
9025 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9026 vik_viewport_sync(t->vvp);
9031 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9033 VikViewport *vvp = t->vvp;
9034 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9035 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9039 if (tool_sync_done) {
9040 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9041 tool_sync_done = FALSE;
9045 static void marker_end_move ( tool_ed_t *t )
9047 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9048 g_object_unref ( t->gc );
9052 /*** Edit waypoint ****/
9054 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9056 tool_ed_t *t = g_new(tool_ed_t, 1);
9062 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9067 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9069 WPSearchParams params;
9070 tool_ed_t *t = data;
9071 VikViewport *vvp = t->vvp;
9073 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9080 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9083 if ( vtl->current_wp && vtl->current_wp->visible )
9085 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9087 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9089 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9090 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9092 if ( event->button == 3 )
9093 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9095 marker_begin_move(t, event->x, event->y);
9102 params.x = event->x;
9103 params.y = event->y;
9104 params.draw_images = vtl->drawimages;
9105 params.closest_wp_id = NULL;
9106 params.closest_wp = NULL;
9107 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9108 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9110 marker_begin_move(t, event->x, event->y);
9113 else if ( params.closest_wp )
9115 if ( event->button == 3 )
9116 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9118 vtl->waypoint_rightclick = FALSE;
9120 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9122 vtl->current_wp = params.closest_wp;
9123 vtl->current_wp_id = params.closest_wp_id;
9125 /* could make it so don't update if old WP is off screen and new is null but oh well */
9126 vik_layer_emit_update ( VIK_LAYER(vtl) );
9130 vtl->current_wp = NULL;
9131 vtl->current_wp_id = NULL;
9132 vtl->waypoint_rightclick = FALSE;
9133 vik_layer_emit_update ( VIK_LAYER(vtl) );
9137 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9139 tool_ed_t *t = data;
9140 VikViewport *vvp = t->vvp;
9142 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9147 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9150 if ( event->state & GDK_CONTROL_MASK )
9152 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9154 new_coord = tp->coord;
9158 if ( event->state & GDK_SHIFT_MASK )
9160 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9161 if ( wp && wp != vtl->current_wp )
9162 new_coord = wp->coord;
9167 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9169 marker_moveto ( t, x, y );
9176 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9178 tool_ed_t *t = data;
9179 VikViewport *vvp = t->vvp;
9181 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9184 if ( t->holding && event->button == 1 )
9187 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9190 if ( event->state & GDK_CONTROL_MASK )
9192 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9194 new_coord = tp->coord;
9198 if ( event->state & GDK_SHIFT_MASK )
9200 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9201 if ( wp && wp != vtl->current_wp )
9202 new_coord = wp->coord;
9205 marker_end_move ( t );
9207 vtl->current_wp->coord = new_coord;
9209 trw_layer_calculate_bounds_waypoints ( vtl );
9210 vik_layer_emit_update ( VIK_LAYER(vtl) );
9213 /* PUT IN RIGHT PLACE!!! */
9214 if ( event->button == 3 && vtl->waypoint_rightclick )
9216 if ( vtl->wp_right_click_menu )
9217 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9218 if ( vtl->current_wp ) {
9219 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9220 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 );
9221 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9223 vtl->waypoint_rightclick = FALSE;
9228 /*** New track ****/
9230 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9237 GdkDrawable *drawable;
9243 * Draw specified pixmap
9245 static gboolean draw_sync ( gpointer data )
9247 draw_sync_t *ds = (draw_sync_t*) data;
9248 // Sometimes don't want to draw
9249 // normally because another update has taken precedent such as panning the display
9250 // which means this pixmap is no longer valid
9251 if ( ds->vtl->draw_sync_do ) {
9252 gdk_threads_enter();
9253 gdk_draw_drawable (ds->drawable,
9256 0, 0, 0, 0, -1, -1);
9257 ds->vtl->draw_sync_done = TRUE;
9258 gdk_threads_leave();
9264 static gchar* distance_string (gdouble distance)
9268 /* draw label with distance */
9269 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9270 switch (dist_units) {
9271 case VIK_UNITS_DISTANCE_MILES:
9272 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9273 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9274 } else if (distance < 1609.4) {
9275 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9277 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9281 // VIK_UNITS_DISTANCE_KILOMETRES
9282 if (distance >= 1000 && distance < 100000) {
9283 g_sprintf(str, "%3.2f km", distance/1000.0);
9284 } else if (distance < 1000) {
9285 g_sprintf(str, "%d m", (int)distance);
9287 g_sprintf(str, "%d km", (int)distance/1000);
9291 return g_strdup (str);
9295 * Actually set the message in statusbar
9297 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9299 // Only show elevation data when track has some elevation properties
9300 gchar str_gain_loss[64];
9301 str_gain_loss[0] = '\0';
9302 gchar str_last_step[64];
9303 str_last_step[0] = '\0';
9304 gchar *str_total = distance_string (distance);
9306 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9307 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9308 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9310 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9313 if ( last_step > 0 ) {
9314 gchar *tmp = distance_string (last_step);
9315 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9319 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9321 // Write with full gain/loss information
9322 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9323 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9325 g_free ( str_total );
9329 * Figure out what information should be set in the statusbar and then write it
9331 static void update_statusbar ( VikTrwLayer *vtl )
9333 // Get elevation data
9334 gdouble elev_gain, elev_loss;
9335 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9337 /* Find out actual distance of current track */
9338 gdouble distance = vik_track_get_length (vtl->current_track);
9340 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9344 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9346 /* if we haven't sync'ed yet, we don't have time to do more. */
9347 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9348 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9350 static GdkPixmap *pixmap = NULL;
9352 // Need to check in case window has been resized
9353 w1 = vik_viewport_get_width(vvp);
9354 h1 = vik_viewport_get_height(vvp);
9356 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9358 gdk_drawable_get_size (pixmap, &w2, &h2);
9359 if (w1 != w2 || h1 != h2) {
9360 g_object_unref ( G_OBJECT ( pixmap ) );
9361 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9364 // Reset to background
9365 gdk_draw_drawable (pixmap,
9366 vtl->current_track_newpoint_gc,
9367 vik_viewport_get_pixmap(vvp),
9368 0, 0, 0, 0, -1, -1);
9370 draw_sync_t *passalong;
9373 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9375 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9376 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9377 // thus when we come to reset to the background it would include what we have already drawn!!
9378 gdk_draw_line ( pixmap,
9379 vtl->current_track_newpoint_gc,
9380 x1, y1, event->x, event->y );
9381 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9383 /* Find out actual distance of current track */
9384 gdouble distance = vik_track_get_length (vtl->current_track);
9386 // Now add distance to where the pointer is //
9389 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9390 vik_coord_to_latlon ( &coord, &ll );
9391 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9392 distance = distance + last_step;
9394 // Get elevation data
9395 gdouble elev_gain, elev_loss;
9396 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9398 // Adjust elevation data (if available) for the current pointer position
9400 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9401 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9402 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9403 // Adjust elevation of last track point
9404 if ( elev_new > last_tpt->altitude )
9406 elev_gain += elev_new - last_tpt->altitude;
9409 elev_loss += last_tpt->altitude - elev_new;
9414 // Display of the distance 'tooltip' during track creation is controlled by a preference
9416 if ( a_vik_get_create_track_tooltip() ) {
9418 gchar *str = distance_string (distance);
9420 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9421 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9422 pango_layout_set_text (pl, str, -1);
9424 pango_layout_get_pixel_size ( pl, &wd, &hd );
9427 // offset from cursor a bit depending on font size
9431 // Create a background block to make the text easier to read over the background map
9432 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9433 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9434 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9436 g_object_unref ( G_OBJECT ( pl ) );
9437 g_object_unref ( G_OBJECT ( background_block_gc ) );
9441 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9442 passalong->vtl = vtl;
9443 passalong->pixmap = pixmap;
9444 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9445 passalong->gc = vtl->current_track_newpoint_gc;
9449 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9451 // Update statusbar with full gain/loss information
9452 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9454 // draw pixmap when we have time to
9455 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9456 vtl->draw_sync_done = FALSE;
9457 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9459 return VIK_LAYER_TOOL_ACK;
9462 // NB vtl->current_track must be valid
9463 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9466 if ( vtl->current_track->trackpoints ) {
9467 // TODO rework this...
9468 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9469 GList *last = g_list_last(vtl->current_track->trackpoints);
9470 g_free ( last->data );
9471 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9473 vik_track_calculate_bounds ( vtl->current_track );
9477 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9479 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9480 vtl->current_track = NULL;
9481 vik_layer_emit_update ( VIK_LAYER(vtl) );
9483 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9484 undo_trackpoint_add ( vtl );
9485 update_statusbar ( vtl );
9486 vik_layer_emit_update ( VIK_LAYER(vtl) );
9493 * Common function to handle trackpoint button requests on either a route or a track
9494 * . enables adding a point via normal click
9495 * . enables removal of last point via right click
9496 * . finishing of the track or route via double clicking
9498 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9502 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9505 if ( event->button == 2 ) {
9506 // As the display is panning, the new track pixmap is now invalid so don't draw it
9507 // otherwise this drawing done results in flickering back to an old image
9508 vtl->draw_sync_do = FALSE;
9512 if ( event->button == 3 )
9514 if ( !vtl->current_track )
9516 undo_trackpoint_add ( vtl );
9517 update_statusbar ( vtl );
9518 vik_layer_emit_update ( VIK_LAYER(vtl) );
9522 if ( event->type == GDK_2BUTTON_PRESS )
9524 /* subtract last (duplicate from double click) tp then end */
9525 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9527 /* undo last, then end */
9528 undo_trackpoint_add ( vtl );
9529 vtl->current_track = NULL;
9531 vik_layer_emit_update ( VIK_LAYER(vtl) );
9535 tp = vik_trackpoint_new();
9536 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9538 /* snap to other TP */
9539 if ( event->state & GDK_CONTROL_MASK )
9541 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9543 tp->coord = other_tp->coord;
9546 tp->newsegment = FALSE;
9547 tp->has_timestamp = FALSE;
9550 if ( vtl->current_track ) {
9551 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9552 /* Auto attempt to get elevation from DEM data (if it's available) */
9553 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9556 vtl->ct_x1 = vtl->ct_x2;
9557 vtl->ct_y1 = vtl->ct_y2;
9558 vtl->ct_x2 = event->x;
9559 vtl->ct_y2 = event->y;
9561 vik_layer_emit_update ( VIK_LAYER(vtl) );
9565 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9567 // ----------------------------------------------------- if current is a route - switch to new track
9568 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9570 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9571 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9573 new_track_create_common ( vtl, name );
9579 return tool_new_track_or_route_click ( vtl, event, vvp );
9582 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9584 if ( event->button == 2 ) {
9585 // Pan moving ended - enable potential point drawing again
9586 vtl->draw_sync_do = TRUE;
9587 vtl->draw_sync_done = TRUE;
9591 /*** New route ****/
9593 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9598 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9600 // -------------------------- if current is a track - switch to new route
9601 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9603 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9604 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9605 new_route_create_common ( vtl, name );
9611 return tool_new_track_or_route_click ( vtl, event, vvp );
9614 /*** New waypoint ****/
9616 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9621 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9624 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9626 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9627 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9628 trw_layer_calculate_bounds_waypoints ( vtl );
9629 vik_layer_emit_update ( VIK_LAYER(vtl) );
9635 /*** Edit trackpoint ****/
9637 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9639 tool_ed_t *t = g_new(tool_ed_t, 1);
9645 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9650 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9652 tool_ed_t *t = data;
9653 VikViewport *vvp = t->vvp;
9654 TPSearchParams params;
9655 /* OUTDATED DOCUMENTATION:
9656 find 5 pixel range on each side. then put these UTM, and a pointer
9657 to the winning track name (and maybe the winning track itself), and a
9658 pointer to the winning trackpoint, inside an array or struct. pass
9659 this along, do a foreach on the tracks which will do a foreach on the
9662 params.x = event->x;
9663 params.y = event->y;
9664 params.closest_track_id = NULL;
9665 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9666 params.closest_tp = NULL;
9667 params.closest_tpl = NULL;
9668 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9670 if ( event->button != 1 )
9673 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9676 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9679 if ( vtl->current_tpl )
9681 /* 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.) */
9682 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9683 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9688 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9690 if ( current_tr->visible &&
9691 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9692 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9693 marker_begin_move ( t, event->x, event->y );
9699 if ( vtl->tracks_visible )
9700 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9702 if ( params.closest_tp )
9704 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9705 vtl->current_tpl = params.closest_tpl;
9706 vtl->current_tp_id = params.closest_track_id;
9707 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9708 trw_layer_tpwin_init ( vtl );
9709 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9710 vik_layer_emit_update ( VIK_LAYER(vtl) );
9714 if ( vtl->routes_visible )
9715 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9717 if ( params.closest_tp )
9719 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9720 vtl->current_tpl = params.closest_tpl;
9721 vtl->current_tp_id = params.closest_track_id;
9722 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9723 trw_layer_tpwin_init ( vtl );
9724 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9725 vik_layer_emit_update ( VIK_LAYER(vtl) );
9729 /* these aren't the droids you're looking for */
9733 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9735 tool_ed_t *t = data;
9736 VikViewport *vvp = t->vvp;
9738 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9744 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9747 if ( event->state & GDK_CONTROL_MASK )
9749 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9750 if ( tp && tp != vtl->current_tpl->data )
9751 new_coord = tp->coord;
9753 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9756 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9757 marker_moveto ( t, x, y );
9765 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9767 tool_ed_t *t = data;
9768 VikViewport *vvp = t->vvp;
9770 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9772 if ( event->button != 1)
9777 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9780 if ( event->state & GDK_CONTROL_MASK )
9782 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9783 if ( tp && tp != vtl->current_tpl->data )
9784 new_coord = tp->coord;
9787 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9788 if ( vtl->current_tp_track )
9789 vik_track_calculate_bounds ( vtl->current_tp_track );
9791 marker_end_move ( t );
9793 /* diff dist is diff from orig */
9795 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9797 vik_layer_emit_update ( VIK_LAYER(vtl) );
9804 /*** Route Finder ***/
9805 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9810 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9813 if ( !vtl ) return FALSE;
9814 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9815 if ( event->button == 3 && vtl->route_finder_current_track ) {
9817 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9819 vtl->route_finder_coord = *new_end;
9821 vik_layer_emit_update ( VIK_LAYER(vtl) );
9822 /* remove last ' to:...' */
9823 if ( vtl->route_finder_current_track->comment ) {
9824 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9825 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9826 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9827 last_to - vtl->route_finder_current_track->comment - 1);
9828 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9833 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9834 struct LatLon start, end;
9836 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9837 vik_coord_to_latlon ( &(tmp), &end );
9838 vtl->route_finder_coord = tmp; /* for continuations */
9840 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9841 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9842 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9844 vtl->route_finder_check_added_track = TRUE;
9845 vtl->route_finder_started = FALSE;
9848 vik_routing_default_find ( vtl, start, end);
9850 /* see if anything was done -- a track was added or appended to */
9851 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9852 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 ) );
9853 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9854 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9855 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9856 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9859 if ( vtl->route_finder_added_track )
9860 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9862 vtl->route_finder_added_track = NULL;
9863 vtl->route_finder_check_added_track = FALSE;
9864 vtl->route_finder_append = FALSE;
9866 vik_layer_emit_update ( VIK_LAYER(vtl) );
9868 vtl->route_finder_started = TRUE;
9869 vtl->route_finder_coord = tmp;
9870 vtl->route_finder_current_track = NULL;
9875 /*** Show picture ****/
9877 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9882 /* Params are: vvp, event, last match found or NULL */
9883 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9885 if ( wp->image && wp->visible )
9887 gint x, y, slackx, slacky;
9888 GdkEventButton *event = (GdkEventButton *) params[1];
9890 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9891 slackx = wp->image_width / 2;
9892 slacky = wp->image_height / 2;
9893 if ( x <= event->x + slackx && x >= event->x - slackx
9894 && y <= event->y + slacky && y >= event->y - slacky )
9896 params[2] = wp->image; /* we've found a match. however continue searching
9897 * since we want to find the last match -- that
9898 * is, the match that was drawn last. */
9903 static void trw_layer_show_picture ( menu_array_sublayer values )
9905 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9907 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
9910 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
9911 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9912 g_free ( quoted_file );
9913 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9915 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() );
9916 g_error_free ( err );
9919 #endif /* WINDOWS */
9922 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9924 gpointer params[3] = { vvp, event, NULL };
9925 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9927 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9930 static menu_array_sublayer values;
9931 values[MA_VTL] = vtl;
9932 values[MA_MISC] = params[2];
9933 trw_layer_show_picture ( values );
9934 return TRUE; /* found a match */
9937 return FALSE; /* go through other layers, searching for a match */
9940 /***************************************************************************
9942 ***************************************************************************/
9945 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9947 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9948 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9951 /* Structure for thumbnail creating data used in the background thread */
9953 VikTrwLayer *vtl; // Layer needed for redrawing
9954 GSList *pics; // Image list
9955 } thumbnail_create_thread_data;
9957 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9959 guint total = g_slist_length(tctd->pics), done = 0;
9960 while ( tctd->pics )
9962 a_thumbnails_create ( (gchar *) tctd->pics->data );
9963 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9965 return -1; /* Abort thread */
9967 tctd->pics = tctd->pics->next;
9970 // Redraw to show the thumbnails as they are now created
9971 if ( IS_VIK_LAYER(tctd->vtl) )
9972 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9977 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9979 while ( tctd->pics )
9981 g_free ( tctd->pics->data );
9982 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9987 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9989 if ( ! vtl->has_verified_thumbnails )
9991 GSList *pics = NULL;
9992 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9995 gint len = g_slist_length ( pics );
9996 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9997 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10000 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10002 (vik_thr_func) create_thumbnails_thread,
10004 (vik_thr_free_func) thumbnail_create_thread_free,
10012 static const gchar* my_track_colors ( gint ii )
10014 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10026 // Fast and reliable way of returning a colour
10027 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10030 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10032 GHashTableIter iter;
10033 gpointer key, value;
10037 g_hash_table_iter_init ( &iter, vtl->tracks );
10039 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10041 // Tracks get a random spread of colours if not already assigned
10042 if ( ! VIK_TRACK(value)->has_color ) {
10043 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10044 VIK_TRACK(value)->color = vtl->track_color;
10046 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10048 VIK_TRACK(value)->has_color = TRUE;
10051 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10054 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10060 g_hash_table_iter_init ( &iter, vtl->routes );
10062 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10064 // Routes get an intermix of reds
10065 if ( ! VIK_TRACK(value)->has_color ) {
10067 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10069 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10070 VIK_TRACK(value)->has_color = TRUE;
10073 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10080 * (Re)Calculate the bounds of the waypoints in this layer,
10081 * This should be called whenever waypoints are changed
10083 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10085 struct LatLon topleft = { 0.0, 0.0 };
10086 struct LatLon bottomright = { 0.0, 0.0 };
10089 GHashTableIter iter;
10090 gpointer key, value;
10092 g_hash_table_iter_init ( &iter, vtl->waypoints );
10094 // Set bounds to first point
10095 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10096 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10097 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10100 // Ensure there is another point...
10101 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10103 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10105 // See if this point increases the bounds.
10106 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10108 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10109 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10110 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10111 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10115 vtl->waypoints_bbox.north = topleft.lat;
10116 vtl->waypoints_bbox.east = bottomright.lon;
10117 vtl->waypoints_bbox.south = bottomright.lat;
10118 vtl->waypoints_bbox.west = topleft.lon;
10121 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10123 vik_track_calculate_bounds ( trk );
10126 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10128 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10129 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10132 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10134 if ( ! VIK_LAYER(vtl)->vt )
10137 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10138 if ( g_hash_table_size (vtl->tracks) > 1 )
10139 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10141 if ( g_hash_table_size (vtl->routes) > 1 )
10142 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10144 if ( g_hash_table_size (vtl->waypoints) > 1 )
10145 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10148 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10150 if ( VIK_LAYER(vtl)->realized )
10151 trw_layer_verify_thumbnails ( vtl, vvp );
10152 trw_layer_track_alloc_colors ( vtl );
10154 trw_layer_calculate_bounds_waypoints ( vtl );
10155 trw_layer_calculate_bounds_tracks ( vtl );
10157 // Apply treeview sort after loading all the tracks for this layer
10158 // (rather than sorted insert on each individual track additional)
10159 // and after subsequent changes to the properties as the specified order may have changed.
10160 // since the sorting of a treeview section is now very quick
10161 // NB sorting is also performed after every name change as well to maintain the list order
10162 trw_layer_sort_all ( vtl );
10164 // Setting metadata time if not otherwise set
10165 if ( vtl->metadata ) {
10167 gboolean need_to_set_time = TRUE;
10168 if ( vtl->metadata->timestamp ) {
10169 need_to_set_time = FALSE;
10170 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10171 need_to_set_time = TRUE;
10174 if ( need_to_set_time ) {
10175 // Could rewrite this as a general get first time of a TRW Layer function
10176 GTimeVal timestamp;
10177 timestamp.tv_usec = 0;
10178 gboolean has_timestamp = FALSE;
10181 gl = g_hash_table_get_values ( vtl->tracks );
10182 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10183 gl = g_list_first ( gl );
10185 // Check times of tracks
10187 // Only need to check the first track as they have been sorted by time
10188 VikTrack *trk = (VikTrack*)gl->data;
10189 // Assume trackpoints already sorted by time
10190 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10191 if ( tpt && tpt->has_timestamp ) {
10192 timestamp.tv_sec = tpt->timestamp;
10193 has_timestamp = TRUE;
10195 g_list_free ( gl );
10198 if ( !has_timestamp ) {
10199 // 'Last' resort - current time
10200 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10201 g_get_current_time ( ×tamp );
10203 // Check times of waypoints
10204 gl = g_hash_table_get_values ( vtl->waypoints );
10206 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10207 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10208 if ( wpt->has_timestamp ) {
10209 if ( timestamp.tv_sec > wpt->timestamp ) {
10210 timestamp.tv_sec = wpt->timestamp;
10211 has_timestamp = TRUE;
10215 g_list_free ( gl );
10218 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10223 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10225 return vtl->coord_mode;
10229 * Uniquify the whole layer
10230 * Also requires the layers panel as the names shown there need updating too
10231 * Returns whether the operation was successful or not
10233 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10235 if ( vtl && vlp ) {
10236 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10237 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10238 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10244 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10246 vik_coord_convert ( &(wp->coord), *dest_mode );
10249 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10251 vik_track_convert ( tr, *dest_mode );
10254 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10256 if ( vtl->coord_mode != dest_mode )
10258 vtl->coord_mode = dest_mode;
10259 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10260 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10261 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10265 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10267 vtl->menu_selection = selection;
10270 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10272 return (vtl->menu_selection);
10275 /* ----------- Downloading maps along tracks --------------- */
10277 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10279 /* TODO: calculating based on current size of viewport */
10280 const gdouble w_at_zoom_0_125 = 0.0013;
10281 const gdouble h_at_zoom_0_125 = 0.0011;
10282 gdouble zoom_factor = zoom_level/0.125;
10284 wh->lat = h_at_zoom_0_125 * zoom_factor;
10285 wh->lon = w_at_zoom_0_125 * zoom_factor;
10287 return 0; /* all OK */
10290 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10292 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10293 (dist->lat >= ABS(to->north_south - from->north_south)))
10296 VikCoord *coord = g_malloc(sizeof(VikCoord));
10297 coord->mode = VIK_COORD_LATLON;
10299 if (ABS(gradient) < 1) {
10300 if (from->east_west > to->east_west)
10301 coord->east_west = from->east_west - dist->lon;
10303 coord->east_west = from->east_west + dist->lon;
10304 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10306 if (from->north_south > to->north_south)
10307 coord->north_south = from->north_south - dist->lat;
10309 coord->north_south = from->north_south + dist->lat;
10310 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10316 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10318 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10319 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10321 VikCoord *next = from;
10323 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10325 list = g_list_prepend(list, next);
10331 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10333 typedef struct _Rect {
10338 #define GLRECT(iter) ((Rect *)((iter)->data))
10341 GList *rects_to_download = NULL;
10344 if (get_download_area_width(vvp, zoom_level, &wh))
10347 GList *iter = tr->trackpoints;
10351 gboolean new_map = TRUE;
10352 VikCoord *cur_coord, tl, br;
10355 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10357 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10358 rect = g_malloc(sizeof(Rect));
10361 rect->center = *cur_coord;
10362 rects_to_download = g_list_prepend(rects_to_download, rect);
10367 gboolean found = FALSE;
10368 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10369 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10380 GList *fillins = NULL;
10381 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10382 /* seems that ATM the function get_next_coord works only for LATLON */
10383 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10384 /* fill-ins for far apart points */
10385 GList *cur_rect, *next_rect;
10386 for (cur_rect = rects_to_download;
10387 (next_rect = cur_rect->next) != NULL;
10388 cur_rect = cur_rect->next) {
10389 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10390 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10391 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10395 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10398 GList *iter = fillins;
10400 cur_coord = (VikCoord *)(iter->data);
10401 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10402 rect = g_malloc(sizeof(Rect));
10405 rect->center = *cur_coord;
10406 rects_to_download = g_list_prepend(rects_to_download, rect);
10411 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10412 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10416 for (iter = fillins; iter; iter = iter->next)
10417 g_free(iter->data);
10418 g_list_free(fillins);
10420 if (rects_to_download) {
10421 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10422 g_free(rect_iter->data);
10423 g_list_free(rects_to_download);
10427 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10431 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10432 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10433 gint selected_zoom, default_zoom;
10435 VikTrwLayer *vtl = values[MA_VTL];
10436 VikLayersPanel *vlp = values[MA_VLP];
10438 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10439 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10441 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10445 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10447 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10448 int num_maps = g_list_length(vmls);
10451 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10455 // Convert from list of vmls to list of names. Allowing the user to select one of them
10456 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10457 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10459 gchar **np = map_names;
10460 VikMapsLayer **lp = map_layers;
10462 for (i = 0; i < num_maps; i++) {
10463 vml = (VikMapsLayer *)(vmls->data);
10465 *np++ = vik_maps_layer_get_map_label(vml);
10468 // Mark end of the array lists
10472 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10473 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10474 if (cur_zoom == zoom_vals[default_zoom])
10477 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10479 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10482 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10485 for (i = 0; i < num_maps; i++)
10486 g_free(map_names[i]);
10488 g_free(map_layers);
10494 /**** lowest waypoint number calculation ***/
10495 static gint highest_wp_number_name_to_number(const gchar *name) {
10496 if ( strlen(name) == 3 ) {
10497 int n = atoi(name);
10498 if ( n < 100 && name[0] != '0' )
10500 if ( n < 10 && name[0] != '0' )
10508 static void highest_wp_number_reset(VikTrwLayer *vtl)
10510 vtl->highest_wp_number = -1;
10513 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10515 /* if is bigger that top, add it */
10516 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10517 if ( new_wp_num > vtl->highest_wp_number )
10518 vtl->highest_wp_number = new_wp_num;
10521 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10523 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10524 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10525 if ( vtl->highest_wp_number == old_wp_num ) {
10527 vtl->highest_wp_number--;
10529 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10530 /* search down until we find something that *does* exist */
10532 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10533 vtl->highest_wp_number--;
10534 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10539 /* get lowest unused number */
10540 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10543 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10545 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10546 return g_strdup(buf);
10550 * trw_layer_create_track_list_both:
10552 * Create the latest list of tracks and routes
10554 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10556 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10557 GList *tracks = NULL;
10558 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10559 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10561 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10564 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10568 gchar *title = NULL;
10569 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10570 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10572 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10574 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10578 static void trw_layer_track_list_dialog ( menu_array_layer values )
10580 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10582 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10583 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10587 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10589 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10591 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10592 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );