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;
152 PangoLayout *tracklabellayout;
153 font_size_t track_font_size;
154 gchar *track_fsize_str;
158 gboolean wp_draw_symbols;
159 font_size_t wp_font_size;
161 vik_layer_sort_order_t wp_sort_order;
163 gdouble track_draw_speed_factor;
165 GdkGC *track_1color_gc;
166 GdkColor track_color;
167 GdkGC *current_track_gc;
168 // Separate GC for a track's potential new point as drawn via separate method
169 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
170 GdkGC *current_track_newpoint_gc;
171 GdkGC *track_bg_gc; GdkColor track_bg_color;
172 GdkGC *waypoint_gc; GdkColor waypoint_color;
173 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
174 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
176 GdkFont *waypoint_font;
177 VikTrack *current_track; // ATM shared between new tracks and new routes
178 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
179 gboolean draw_sync_done;
180 gboolean draw_sync_do;
182 VikCoordMode coord_mode;
184 /* wp editing tool */
185 VikWaypoint *current_wp;
186 gpointer current_wp_id;
188 gboolean waypoint_rightclick;
190 /* track editing tool */
192 VikTrack *current_tp_track;
193 gpointer current_tp_id;
194 VikTrwLayerTpwin *tpwin;
196 /* track editing tool -- more specifically, moving tps */
199 /* route finder tool */
200 gboolean route_finder_started;
201 VikCoord route_finder_coord;
202 gboolean route_finder_check_added_track;
203 VikTrack *route_finder_added_track;
204 VikTrack *route_finder_current_track;
205 gboolean route_finder_append;
212 guint16 image_cache_size;
214 /* for waypoint text */
215 PangoLayout *wplabellayout;
217 gboolean has_verified_thumbnails;
219 GtkMenu *wp_right_click_menu;
220 GtkMenu *track_right_click_menu;
223 VikStdLayerMenuItem menu_selection;
225 gint highest_wp_number;
228 GtkWidget *tracks_analysis_dialog;
231 /* A caached waypoint image. */
234 gchar *image; /* filename */
237 struct DrawingParams {
242 guint16 width, height;
243 gdouble cc; // Cosine factor in track directions
244 gdouble ss; // Sine factor in track directions
245 const VikCoord *center;
246 gboolean one_zone, lat_lon;
247 gdouble ce1, ce2, cn1, cn2;
251 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
253 static void trw_layer_delete_item ( gpointer pass_along[6] );
254 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
255 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
257 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
258 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
260 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
261 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
263 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
264 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
266 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
267 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
268 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
269 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
270 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
271 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
272 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
273 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
274 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
275 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
276 static void trw_layer_append_track ( gpointer pass_along[6] );
277 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
278 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
279 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
280 static void trw_layer_split_segments ( gpointer pass_along[6] );
281 static void trw_layer_delete_point_selected ( gpointer pass_along[6] );
282 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
283 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
284 static void trw_layer_reverse ( gpointer pass_along[6] );
285 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
286 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
287 static void trw_layer_show_picture ( gpointer pass_along[6] );
288 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
290 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
291 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
292 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
293 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
294 static void trw_layer_new_wp ( gpointer lav[2] );
295 static void trw_layer_new_track ( gpointer lav[2] );
296 static void trw_layer_new_route ( gpointer lav[2] );
297 static void trw_layer_finish_track ( gpointer lav[2] );
298 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
299 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
300 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
301 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
302 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
303 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
304 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
305 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
306 #ifdef VIK_CONFIG_GEOTAG
307 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
308 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
309 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
310 static void trw_layer_geotagging ( gpointer lav[2] );
312 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
313 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
314 static void trw_layer_acquire_url_cb ( gpointer lav[2] );
315 #ifdef VIK_CONFIG_OPENSTREETMAP
316 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
317 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
319 #ifdef VIK_CONFIG_GEOCACHES
320 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
322 #ifdef VIK_CONFIG_GEOTAG
323 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
325 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
326 static void trw_layer_gps_upload ( gpointer lav[2] );
328 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] );
329 static void trw_layer_track_list_dialog ( gpointer lav[2] );
330 static void trw_layer_waypoint_list_dialog ( gpointer lav[2] );
332 // Specific route versions:
333 // Most track handling functions can handle operating on the route list
334 // However these ones are easier in separate functions
335 static void trw_layer_auto_routes_view ( gpointer lav[2] );
336 static void trw_layer_delete_all_routes ( gpointer lav[2] );
337 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
340 static void trw_layer_properties_item ( gpointer pass_along[7] );
341 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
342 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
343 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
345 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
346 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
347 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
349 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
350 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
351 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
352 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
354 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
355 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
356 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
357 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
358 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
359 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
360 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
361 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
362 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
363 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
364 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
365 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
366 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
367 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
368 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
369 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
370 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
371 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
372 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
373 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
374 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
375 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
376 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
378 static void cached_pixbuf_free ( CachedPixbuf *cp );
379 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
381 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
382 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
384 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
385 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
387 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
388 static void highest_wp_number_reset(VikTrwLayer *vtl);
389 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
390 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
392 // Note for the following tool GtkRadioActionEntry texts:
393 // the very first text value is an internal name not displayed anywhere
394 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
395 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
396 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
397 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
398 static VikToolInterface trw_layer_tools[] = {
399 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
400 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
401 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
403 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
405 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
406 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
407 (VikToolMouseFunc) tool_new_track_click,
408 (VikToolMouseMoveFunc) tool_new_track_move,
409 (VikToolMouseFunc) tool_new_track_release,
410 (VikToolKeyFunc) tool_new_track_key_press,
411 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
412 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
414 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
415 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
416 (VikToolMouseFunc) tool_new_route_click,
417 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
418 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
419 (VikToolKeyFunc) tool_new_track_key_press, // -/#
420 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
421 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
423 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
424 (VikToolConstructorFunc) tool_edit_waypoint_create,
425 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
427 (VikToolMouseFunc) tool_edit_waypoint_click,
428 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
429 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
431 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
433 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
434 (VikToolConstructorFunc) tool_edit_trackpoint_create,
435 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
437 (VikToolMouseFunc) tool_edit_trackpoint_click,
438 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
439 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
441 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
443 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
444 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
445 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
447 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
449 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
450 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
451 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
453 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
457 TOOL_CREATE_WAYPOINT=0,
461 TOOL_EDIT_TRACKPOINT,
467 /****** PARAMETERS ******/
469 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
470 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
472 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
473 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
475 #define MIN_POINT_SIZE 2
476 #define MAX_POINT_SIZE 10
478 #define MIN_ARROW_SIZE 3
479 #define MAX_ARROW_SIZE 20
481 static VikLayerParamScale params_scales[] = {
482 /* min max step digits */
483 { 1, 10, 1, 0 }, /* line_thickness */
484 { 0, 100, 1, 0 }, /* track draw speed factor */
485 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
486 /* 5 * step == how much to turn */
487 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
488 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
489 { 5, 500, 5, 0 }, // 5: image cache_size - " "
490 { 0, 8, 1, 0 }, // 6: Background line thickness
491 { 1, 64, 1, 0 }, /* wpsize */
492 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
493 { 1, 100, 1, 0 }, // 9: elevation factor
494 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
495 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
498 static gchar* params_font_sizes[] = {
499 N_("Extra Extra Small"),
505 N_("Extra Extra Large"),
508 // Needs to align with vik_layer_sort_order_t
509 static gchar* params_sort_order[] = {
511 N_("Name Ascending"),
512 N_("Name Descending"),
516 static VikLayerParamData black_color_default ( void ) {
517 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
519 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
520 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
521 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
522 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
523 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
524 static VikLayerParamData trackbgcolor_default ( void ) {
525 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
527 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
528 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
529 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
531 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
532 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
533 static VikLayerParamData wptextcolor_default ( void ) {
534 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
536 static VikLayerParamData wpbgcolor_default ( void ) {
537 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
539 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
540 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
542 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
543 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
544 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
546 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
548 VikLayerParam trw_layer_params[] = {
549 { 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 },
550 { 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 },
551 { 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 },
553 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
554 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
555 { 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 },
556 { 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 },
557 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
558 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
559 { 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 },
560 { 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 },
561 { 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 },
562 { 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 },
563 { 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 },
564 { 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 },
565 { 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 },
566 { 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 },
567 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
568 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 },
569 { 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 },
571 { 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 },
572 { 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 },
573 { 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,
574 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
575 { 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 },
577 { 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 },
578 { 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 },
579 { 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 },
580 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
581 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
582 { 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 },
583 { 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 },
584 { 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 },
585 { 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 },
586 { 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 },
588 { 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 },
589 { 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 },
590 { 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 },
591 { 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 },
594 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
596 // Sublayer visibilities
639 *** 1) Add to trw_layer_params and enumeration
640 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
643 /****** END PARAMETERS ******/
645 /* Layer Interface function definitions */
646 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
647 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
648 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
649 static void trw_layer_free ( VikTrwLayer *trwlayer );
650 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
651 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
652 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
653 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
654 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
655 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
656 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
657 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
658 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
659 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
660 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
661 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
662 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
663 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
664 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
665 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
666 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
667 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
668 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
669 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
670 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
671 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
672 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
673 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
674 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
675 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
676 /* End Layer Interface function definitions */
678 VikLayerInterface vik_trw_layer_interface = {
685 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
689 params_groups, /* params_groups */
690 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
694 (VikLayerFuncCreate) trw_layer_create,
695 (VikLayerFuncRealize) trw_layer_realize,
696 (VikLayerFuncPostRead) trw_layer_post_read,
697 (VikLayerFuncFree) trw_layer_free,
699 (VikLayerFuncProperties) NULL,
700 (VikLayerFuncDraw) trw_layer_draw,
701 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
703 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
704 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
706 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
707 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
709 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
710 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
711 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
712 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
713 (VikLayerFuncLayerSelected) trw_layer_selected,
715 (VikLayerFuncMarshall) trw_layer_marshall,
716 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
718 (VikLayerFuncSetParam) trw_layer_set_param,
719 (VikLayerFuncGetParam) trw_layer_get_param,
720 (VikLayerFuncChangeParam) trw_layer_change_param,
722 (VikLayerFuncReadFileData) a_gpspoint_read_file,
723 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
725 (VikLayerFuncDeleteItem) trw_layer_del_item,
726 (VikLayerFuncCutItem) trw_layer_cut_item,
727 (VikLayerFuncCopyItem) trw_layer_copy_item,
728 (VikLayerFuncPasteItem) trw_layer_paste_item,
729 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
731 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
733 (VikLayerFuncSelectClick) trw_layer_select_click,
734 (VikLayerFuncSelectMove) trw_layer_select_move,
735 (VikLayerFuncSelectRelease) trw_layer_select_release,
736 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
739 GType vik_trw_layer_get_type ()
741 static GType vtl_type = 0;
745 static const GTypeInfo vtl_info =
747 sizeof (VikTrwLayerClass),
748 NULL, /* base_init */
749 NULL, /* base_finalize */
750 NULL, /* class init */
751 NULL, /* class_finalize */
752 NULL, /* class_data */
753 sizeof (VikTrwLayer),
755 NULL /* instance init */
757 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
763 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
765 static gpointer pass_along[6];
771 pass_along[1] = NULL;
772 pass_along[2] = GINT_TO_POINTER (subtype);
773 pass_along[3] = sublayer;
774 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
775 pass_along[5] = NULL;
777 trw_layer_delete_item ( pass_along );
780 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
782 static gpointer pass_along[6];
788 pass_along[1] = NULL;
789 pass_along[2] = GINT_TO_POINTER (subtype);
790 pass_along[3] = sublayer;
791 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
792 pass_along[5] = NULL;
794 trw_layer_copy_item_cb(pass_along);
795 trw_layer_cut_item_cb(pass_along);
798 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
800 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
801 gint subtype = GPOINTER_TO_INT (pass_along[2]);
802 gpointer * sublayer = pass_along[3];
806 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
810 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
811 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
812 if ( wp && wp->name )
815 name = NULL; // Broken :(
817 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
818 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
819 if ( trk && trk->name )
822 name = NULL; // Broken :(
825 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
826 if ( trk && trk->name )
829 name = NULL; // Broken :(
832 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
833 subtype, len, name, data);
837 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
839 trw_layer_copy_item_cb(pass_along);
840 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
841 trw_layer_delete_item(pass_along);
844 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
846 // Slightly cheating method, routing via the panels capability
847 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
850 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
860 GByteArray *ba = g_byte_array_new ();
862 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
863 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
864 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
865 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
867 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
870 g_byte_array_append ( ba, id, il );
878 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
885 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
889 w = vik_waypoint_unmarshall ( item, len );
890 // When copying - we'll create a new name based on the original
891 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
892 vik_trw_layer_add_waypoint ( vtl, name, w );
893 waypoint_convert (NULL, w, &vtl->coord_mode);
896 trw_layer_calculate_bounds_waypoints ( vtl );
898 // Consider if redraw necessary for the new item
899 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
900 vik_layer_emit_update ( VIK_LAYER(vtl) );
903 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
907 t = vik_track_unmarshall ( item, len );
908 // When copying - we'll create a new name based on the original
909 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
910 vik_trw_layer_add_track ( vtl, name, t );
911 vik_track_convert (t, vtl->coord_mode);
914 // Consider if redraw necessary for the new item
915 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
916 vik_layer_emit_update ( VIK_LAYER(vtl) );
919 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
923 t = vik_track_unmarshall ( item, len );
924 // When copying - we'll create a new name based on the original
925 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
926 vik_trw_layer_add_route ( vtl, name, t );
927 vik_track_convert (t, vtl->coord_mode);
930 // Consider if redraw necessary for the new item
931 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
932 vik_layer_emit_update ( VIK_LAYER(vtl) );
938 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
945 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
949 case PARAM_TV: vtl->tracks_visible = data.b; break;
950 case PARAM_WV: vtl->waypoints_visible = data.b; break;
951 case PARAM_RV: vtl->routes_visible = data.b; break;
952 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
953 case PARAM_TLFONTSIZE:
954 if ( data.u < FS_NUM_SIZES ) {
955 vtl->track_font_size = data.u;
956 g_free ( vtl->track_fsize_str );
957 switch ( vtl->track_font_size ) {
958 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
959 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
960 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
961 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
962 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
963 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
964 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
968 case PARAM_DM: vtl->drawmode = data.u; break;
970 vtl->track_color = data.c;
971 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
973 case PARAM_DP: vtl->drawpoints = data.b; break;
975 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
976 vtl->drawpoints_size = data.u;
978 case PARAM_DE: vtl->drawelevation = data.b; break;
979 case PARAM_DS: vtl->drawstops = data.b; break;
980 case PARAM_DL: vtl->drawlines = data.b; break;
981 case PARAM_DD: vtl->drawdirections = data.b; break;
983 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
984 vtl->drawdirections_size = data.u;
986 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
987 vtl->stop_length = data.u;
989 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
990 vtl->elevation_factor = data.u;
992 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
994 vtl->line_thickness = data.u;
995 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
998 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1000 vtl->bg_line_thickness = data.u;
1001 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1005 vtl->track_bg_color = data.c;
1006 if ( vtl->track_bg_gc )
1007 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1009 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1010 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1011 case PARAM_DLA: vtl->drawlabels = data.b; break;
1012 case PARAM_DI: vtl->drawimages = data.b; break;
1013 case PARAM_IS: if ( data.u != vtl->image_size )
1015 vtl->image_size = data.u;
1016 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1017 g_queue_free ( vtl->image_cache );
1018 vtl->image_cache = g_queue_new ();
1021 case PARAM_IA: vtl->image_alpha = data.u; break;
1022 case PARAM_ICS: vtl->image_cache_size = data.u;
1023 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1024 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1027 vtl->waypoint_color = data.c;
1028 if ( vtl->waypoint_gc )
1029 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1032 vtl->waypoint_text_color = data.c;
1033 if ( vtl->waypoint_text_gc )
1034 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1037 vtl->waypoint_bg_color = data.c;
1038 if ( vtl->waypoint_bg_gc )
1039 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1042 vtl->wpbgand = data.b;
1043 if ( vtl->waypoint_bg_gc )
1044 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1046 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1047 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1048 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1049 case PARAM_WPFONTSIZE:
1050 if ( data.u < FS_NUM_SIZES ) {
1051 vtl->wp_font_size = data.u;
1052 g_free ( vtl->wp_fsize_str );
1053 switch ( vtl->wp_font_size ) {
1054 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1055 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1056 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1057 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1058 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1059 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1060 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1064 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1069 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1071 VikLayerParamData rv;
1074 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1075 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1076 case PARAM_RV: rv.b = vtl->routes_visible; break;
1077 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1078 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1079 case PARAM_DM: rv.u = vtl->drawmode; break;
1080 case PARAM_TC: rv.c = vtl->track_color; break;
1081 case PARAM_DP: rv.b = vtl->drawpoints; break;
1082 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1083 case PARAM_DE: rv.b = vtl->drawelevation; break;
1084 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1085 case PARAM_DS: rv.b = vtl->drawstops; break;
1086 case PARAM_SL: rv.u = vtl->stop_length; break;
1087 case PARAM_DL: rv.b = vtl->drawlines; break;
1088 case PARAM_DD: rv.b = vtl->drawdirections; break;
1089 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1090 case PARAM_LT: rv.u = vtl->line_thickness; break;
1091 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1092 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1093 case PARAM_DI: rv.b = vtl->drawimages; break;
1094 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1095 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1096 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1097 case PARAM_IS: rv.u = vtl->image_size; break;
1098 case PARAM_IA: rv.u = vtl->image_alpha; break;
1099 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1100 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1101 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1102 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1103 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1104 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1105 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1106 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1107 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1108 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1113 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1115 // This '-3' is to account for the first few parameters not in the properties
1116 const gint OFFSET = -3;
1118 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1119 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1122 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1123 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1124 GtkWidget **ww2 = values[UI_CHG_LABELS];
1125 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1126 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1127 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1128 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1129 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1130 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1131 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1132 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1133 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1134 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1135 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1136 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1139 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1142 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1143 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1144 GtkWidget **ww2 = values[UI_CHG_LABELS];
1145 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1146 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1147 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1148 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1149 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1150 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1151 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1152 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1153 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1154 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1155 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1156 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1157 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1158 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1159 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1160 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1163 // Alter sensitivity of all track colours according to the draw track mode.
1166 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1167 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1168 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1169 GtkWidget **ww2 = values[UI_CHG_LABELS];
1170 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1171 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1172 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1173 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1176 // NB Since other track settings have been split across tabs,
1177 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1182 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1189 // Use byte arrays to store sublayer data
1190 // much like done elsewhere e.g. vik_layer_marshall_params()
1191 GByteArray *ba = g_byte_array_new ( );
1196 guint object_length;
1199 // the length of the item
1200 // the sublayer type of item
1201 // the the actual item
1202 #define tlm_append(object_pointer, size, type) \
1204 object_length = (size); \
1205 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1206 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1207 g_byte_array_append ( ba, (object_pointer), object_length );
1209 // Layer parameters first
1210 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1211 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1212 g_byte_array_append ( ba, pd, pl );
1215 // Now sublayer data
1216 GHashTableIter iter;
1217 gpointer key, value;
1220 g_hash_table_iter_init ( &iter, vtl->waypoints );
1221 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1222 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1223 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1228 g_hash_table_iter_init ( &iter, vtl->tracks );
1229 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1230 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1231 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1236 g_hash_table_iter_init ( &iter, vtl->routes );
1237 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1238 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1239 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1249 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1251 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1253 gint consumed_length;
1255 // First the overall layer parameters
1256 memcpy(&pl, data, sizeof(pl));
1258 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1261 consumed_length = pl;
1262 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1264 #define tlm_size (*(gint *)data)
1265 // See marshalling above for order of how this is written
1267 data += sizeof_len_and_subtype + tlm_size;
1269 // Now the individual sublayers:
1271 while ( *data && consumed_length < len ) {
1272 // Normally four extra bytes at the end of the datastream
1273 // (since it's a GByteArray and that's where it's length is stored)
1274 // So only attempt read when there's an actual block of sublayer data
1275 if ( consumed_length + tlm_size < len ) {
1277 // Reuse pl to read the subtype from the data stream
1278 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1280 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1281 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1282 gchar *name = g_strdup ( trk->name );
1283 vik_trw_layer_add_track ( vtl, name, trk );
1286 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1287 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1288 gchar *name = g_strdup ( wp->name );
1289 vik_trw_layer_add_waypoint ( vtl, name, wp );
1292 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1293 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1294 gchar *name = g_strdup ( trk->name );
1295 vik_trw_layer_add_route ( vtl, name, trk );
1299 consumed_length += tlm_size + sizeof_len_and_subtype;
1302 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1304 // Not stored anywhere else so need to regenerate
1305 trw_layer_calculate_bounds_waypoints ( vtl );
1310 // Keep interesting hash function at least visible
1312 static guint strcase_hash(gconstpointer v)
1314 // 31 bit hash function
1317 gchar s[128]; // malloc is too slow for reading big files
1320 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1321 p[i] = toupper(t[i]);
1327 for (p += 1; *p != '\0'; p++)
1328 h = (h << 5) - h + *p;
1335 // Stick a 1 at the end of the function name to make it more unique
1336 // thus more easily searchable in a simple text editor
1337 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1339 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1340 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1342 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1343 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1345 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1346 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1347 // and with normal PC processing capabilities - it has negligibile performance impact
1348 // This also minimized the amount of rework - as the management of the hash tables already exists.
1350 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1351 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1352 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1354 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1355 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1356 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1357 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1358 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1359 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1361 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1363 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1365 // Param settings that are not available via the GUI
1366 // Force to on after processing params (which defaults them to off with a zero value)
1367 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1369 rv->draw_sync_done = TRUE;
1370 rv->draw_sync_do = TRUE;
1371 // Everything else is 0, FALSE or NULL
1377 static void trw_layer_free ( VikTrwLayer *trwlayer )
1379 g_hash_table_destroy(trwlayer->waypoints);
1380 g_hash_table_destroy(trwlayer->waypoints_iters);
1381 g_hash_table_destroy(trwlayer->tracks);
1382 g_hash_table_destroy(trwlayer->tracks_iters);
1383 g_hash_table_destroy(trwlayer->routes);
1384 g_hash_table_destroy(trwlayer->routes_iters);
1386 /* ODC: replace with GArray */
1387 trw_layer_free_track_gcs ( trwlayer );
1389 if ( trwlayer->wp_right_click_menu )
1390 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1392 if ( trwlayer->track_right_click_menu )
1393 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1395 if ( trwlayer->tracklabellayout != NULL)
1396 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1398 if ( trwlayer->wplabellayout != NULL)
1399 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1401 if ( trwlayer->waypoint_gc != NULL )
1402 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1404 if ( trwlayer->waypoint_text_gc != NULL )
1405 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1407 if ( trwlayer->waypoint_bg_gc != NULL )
1408 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1410 g_free ( trwlayer->wp_fsize_str );
1411 g_free ( trwlayer->track_fsize_str );
1413 if ( trwlayer->tpwin != NULL )
1414 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1416 if ( trwlayer->tracks_analysis_dialog != NULL )
1417 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1419 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1420 g_queue_free ( trwlayer->image_cache );
1423 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1427 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1428 dp->xmpp = vik_viewport_get_xmpp ( vp );
1429 dp->ympp = vik_viewport_get_ympp ( vp );
1430 dp->width = vik_viewport_get_width ( vp );
1431 dp->height = vik_viewport_get_height ( vp );
1432 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1433 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1435 dp->center = vik_viewport_get_center ( vp );
1436 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1437 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1442 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1443 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1444 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1446 dp->ce1 = dp->center->east_west-w2;
1447 dp->ce2 = dp->center->east_west+w2;
1448 dp->cn1 = dp->center->north_south-h2;
1449 dp->cn2 = dp->center->north_south+h2;
1450 } else if ( dp->lat_lon ) {
1451 VikCoord upperleft, bottomright;
1452 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1453 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1454 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1455 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1456 dp->ce1 = upperleft.east_west;
1457 dp->ce2 = bottomright.east_west;
1458 dp->cn1 = bottomright.north_south;
1459 dp->cn2 = upperleft.north_south;
1462 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1466 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1467 * Here a simple traffic like light colour system is used:
1468 * . slow points are red
1469 * . average is yellow
1470 * . fast points are green
1472 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1475 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1476 if ( average_speed > 0 ) {
1477 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1478 if ( rv < low_speed )
1479 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1480 else if ( rv > high_speed )
1481 return VIK_TRW_LAYER_TRACK_GC_FAST;
1483 return VIK_TRW_LAYER_TRACK_GC_AVER;
1486 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1489 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1491 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1492 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1493 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1494 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1498 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1500 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1502 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1503 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1505 // Fallback if parse failure
1506 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1508 g_free ( label_markup );
1510 gint label_x, label_y;
1512 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1514 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1515 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1519 * distance_in_preferred_units:
1520 * @dist: The source distance in standard SI Units (i.e. metres)
1522 * TODO: This is a generic function that could be moved into globals.c or utils.c
1524 * Probably best used if you have a only few conversions to perform.
1525 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1526 * since it will be doing the preference check on each call
1528 * Returns: The distance in the units as specified by the preferences
1530 static gdouble distance_in_preferred_units ( gdouble dist )
1533 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1534 switch (dist_units) {
1535 case VIK_UNITS_DISTANCE_MILES:
1536 mydist = VIK_METERS_TO_MILES(dist);
1538 // VIK_UNITS_DISTANCE_KILOMETRES:
1540 mydist = dist/1000.0;
1547 * trw_layer_draw_dist_labels:
1549 * Draw a few labels along a track at nicely seperated distances
1550 * This might slow things down if there's many tracks being displayed with this on.
1552 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1554 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1555 25.0, 40.0, 50.0, 75.0, 100.0,
1556 150.0, 200.0, 250.0, 500.0, 1000.0};
1558 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1560 // Convert to specified unit to find the friendly breakdown value
1561 dist = distance_in_preferred_units ( dist );
1565 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1566 if ( chunksd[i] > dist ) {
1568 dist = chunksd[index];
1573 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1575 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1576 gdouble dist_i = dist * i;
1578 // Convert distance back into metres for use in finding a trackpoint
1579 switch (dist_units) {
1580 case VIK_UNITS_DISTANCE_MILES:
1581 dist_i = VIK_MILES_TO_METERS(dist_i);
1583 // VIK_UNITS_DISTANCE_KILOMETRES:
1585 dist_i = dist_i*1000.0;
1589 gdouble dist_current = 0.0;
1590 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1591 gdouble dist_next = 0.0;
1592 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1594 gdouble dist_between_tps = fabs (dist_next - dist_current);
1595 gdouble ratio = 0.0;
1596 // Prevent division by 0 errors
1597 if ( dist_between_tps > 0.0 )
1598 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1600 if ( tp_current && tp_next ) {
1601 // Construct the name based on the distance value
1604 switch (dist_units) {
1605 case VIK_UNITS_DISTANCE_MILES:
1606 units = g_strdup ( _("miles") );
1608 // VIK_UNITS_DISTANCE_KILOMETRES:
1610 units = g_strdup ( _("km") );
1614 // Convert for display
1615 dist_i = distance_in_preferred_units ( dist_i );
1617 // Make the precision of the output related to the unit size.
1619 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1620 else if ( index == 1 )
1621 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1623 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1626 struct LatLon ll_current, ll_next;
1627 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1628 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1630 // positional interpolation
1631 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1632 // but should be good enough over the small scale that I anticipate usage on
1633 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1634 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1636 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1639 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1640 fgcolour = gdk_color_to_string ( &(trk->color) );
1642 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1644 // if highlight mode on, then colour the background in the highlight colour
1646 if ( drawing_highlight )
1647 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1649 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1651 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1653 g_free ( fgcolour );
1654 g_free ( bgcolour );
1661 * trw_layer_draw_track_name_labels:
1663 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1665 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1668 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1669 fgcolour = gdk_color_to_string ( &(trk->color) );
1671 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1673 // if highlight mode on, then colour the background in the highlight colour
1675 if ( drawing_highlight )
1676 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1678 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1680 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1682 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1683 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1684 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1685 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1686 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1687 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1689 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1691 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1694 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1695 // No other labels to draw
1698 GList *ending = g_list_last ( trk->trackpoints );
1699 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1700 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1702 gboolean done_start_end = FALSE;
1704 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1705 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1707 // This number can be configured via the settings if you really want to change it
1708 gdouble distance_diff;
1709 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1710 distance_diff = 100.0; // Metres
1712 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1713 // Start and end 'close' together so only draw one label at an average location
1714 gint x1, x2, y1, y2;
1715 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1716 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1718 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1720 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1721 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1724 done_start_end = TRUE;
1728 if ( ! done_start_end ) {
1729 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1730 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1731 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1732 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1733 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1734 g_free ( name_start );
1736 // Don't draw end label if this is the one being created
1737 if ( trk != dp->vtl->current_track ) {
1738 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1739 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1740 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1741 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1742 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1743 g_free ( name_end );
1748 g_free ( fgcolour );
1749 g_free ( bgcolour );
1753 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1755 if ( ! track->visible )
1758 /* TODO: this function is a mess, get rid of any redundancy */
1759 GList *list = track->trackpoints;
1761 gboolean useoldvals = TRUE;
1763 gboolean drawpoints;
1765 gboolean drawelevation;
1766 gdouble min_alt, max_alt, alt_diff = 0;
1768 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1769 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1772 if ( dp->vtl->drawelevation )
1774 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1775 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1776 alt_diff = max_alt - min_alt;
1779 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1780 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1781 trw_layer_draw_track ( id, track, dp, TRUE );
1783 if ( draw_track_outline )
1784 drawpoints = drawstops = FALSE;
1786 drawpoints = dp->vtl->drawpoints;
1787 drawstops = dp->vtl->drawstops;
1790 gboolean drawing_highlight = FALSE;
1791 /* Current track - used for creation */
1792 if ( track == dp->vtl->current_track )
1793 main_gc = dp->vtl->current_track_gc;
1795 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1796 /* Draw all tracks of the layer in special colour */
1797 /* if track is member of selected layer or is the current selected track
1798 then draw in the highlight colour.
1799 NB this supercedes the drawmode */
1800 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1801 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1802 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1803 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1804 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1805 drawing_highlight = TRUE;
1808 if ( !drawing_highlight ) {
1809 // Still need to figure out the gc according to the drawing mode:
1810 switch ( dp->vtl->drawmode ) {
1811 case DRAWMODE_BY_TRACK:
1812 if ( dp->vtl->track_1color_gc )
1813 g_object_unref ( dp->vtl->track_1color_gc );
1814 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1815 main_gc = dp->vtl->track_1color_gc;
1818 // Mostly for DRAWMODE_ALL_SAME_COLOR
1819 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1820 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1827 int x, y, oldx, oldy;
1828 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1830 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1832 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1834 // Draw the first point as something a bit different from the normal points
1835 // ATM it's slightly bigger and a triangle
1837 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1838 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1844 gdouble average_speed = 0.0;
1845 gdouble low_speed = 0.0;
1846 gdouble high_speed = 0.0;
1847 // If necessary calculate these values - which is done only once per track redraw
1848 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1849 // the percentage factor away from the average speed determines transistions between the levels
1850 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1851 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1852 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1855 while ((list = g_list_next(list)))
1857 tp = VIK_TRACKPOINT(list->data);
1858 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1860 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1861 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1862 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1863 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1864 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1866 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1869 * If points are the same in display coordinates, don't draw.
1871 if ( useoldvals && x == oldx && y == oldy )
1873 // Still need to process points to ensure 'stops' are drawn if required
1874 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1875 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1876 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 );
1881 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1882 if ( drawpoints || dp->vtl->drawlines ) {
1883 // setup main_gc for both point and line drawing
1884 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1885 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 ) );
1889 if ( drawpoints && ! draw_track_outline )
1894 * The concept of drawing stops is that a trackpoint
1895 * that is if the next trackpoint has a timestamp far into
1896 * the future, we draw a circle of 6x trackpoint size,
1897 * instead of a rectangle of 2x trackpoint size.
1898 * This is drawn first so the trackpoint will be drawn on top
1901 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1902 /* Stop point. Draw 6x circle. Always in redish colour */
1903 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 );
1905 /* Regular point - draw 2x square. */
1906 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1909 /* Final point - draw 4x circle. */
1910 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 );
1913 if ((!tp->newsegment) && (dp->vtl->drawlines))
1916 /* UTM only: zone check */
1917 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1918 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1921 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1923 if ( draw_track_outline ) {
1924 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1928 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1930 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1932 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1937 tmp[1].y = oldy-FIXALTITUDE(list->data);
1939 tmp[2].y = y-FIXALTITUDE(list->next->data);
1944 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1945 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1947 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1948 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1950 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1955 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1956 // Draw an arrow at the mid point to show the direction of the track
1957 // Code is a rework from vikwindow::draw_ruler()
1958 gint midx = (oldx + x) / 2;
1959 gint midy = (oldy + y) / 2;
1961 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1962 // Avoid divide by zero and ensure at least 1 pixel big
1964 gdouble dx = (oldx - midx) / len;
1965 gdouble dy = (oldy - midy) / len;
1966 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1967 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1977 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1979 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1980 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1982 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1984 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1985 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 ));
1989 * If points are the same in display coordinates, don't draw.
1991 if ( x != oldx || y != oldy )
1993 if ( draw_track_outline )
1994 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1996 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2002 * If points are the same in display coordinates, don't draw.
2004 if ( x != oldx && y != oldy )
2006 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2007 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2015 // Labels drawn after the trackpoints, so the labels are on top
2016 if ( dp->vtl->track_draw_labels ) {
2017 if ( track->max_number_dist_labels > 0 ) {
2018 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2021 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2022 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2028 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2030 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2031 trw_layer_draw_track ( id, track, dp, FALSE );
2035 static void cached_pixbuf_free ( CachedPixbuf *cp )
2037 g_object_unref ( G_OBJECT(cp->pixbuf) );
2038 g_free ( cp->image );
2041 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2043 return strcmp ( cp->image, name );
2046 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2049 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2050 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2051 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2054 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2056 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2058 if ( wp->image && dp->vtl->drawimages )
2060 GdkPixbuf *pixbuf = NULL;
2063 if ( dp->vtl->image_alpha == 0)
2066 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2068 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2071 gchar *image = wp->image;
2072 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2073 if ( ! regularthumb )
2075 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2076 image = "\x12\x00"; /* this shouldn't occur naturally. */
2080 CachedPixbuf *cp = NULL;
2081 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2082 if ( dp->vtl->image_size == 128 )
2083 cp->pixbuf = regularthumb;
2086 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2087 g_assert ( cp->pixbuf );
2088 g_object_unref ( G_OBJECT(regularthumb) );
2090 cp->image = g_strdup ( image );
2092 /* needed so 'click picture' tool knows how big the pic is; we don't
2093 * store it in cp because they may have been freed already. */
2094 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2095 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2097 g_queue_push_head ( dp->vtl->image_cache, cp );
2098 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2099 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2101 pixbuf = cp->pixbuf;
2105 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2111 w = gdk_pixbuf_get_width ( pixbuf );
2112 h = gdk_pixbuf_get_height ( pixbuf );
2114 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2116 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2117 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2118 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2119 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2120 // Highlighted - so draw a little border around the chosen one
2121 // single line seems a little weak so draw 2 of them
2122 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2123 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2124 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2125 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2128 if ( dp->vtl->image_alpha == 255 )
2129 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2131 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2133 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2137 // Draw appropriate symbol - either symbol image or simple types
2138 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2139 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 );
2141 else if ( wp == dp->vtl->current_wp ) {
2142 switch ( dp->vtl->wp_symbol ) {
2143 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;
2144 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;
2145 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;
2146 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 );
2147 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 );
2151 switch ( dp->vtl->wp_symbol ) {
2152 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;
2153 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;
2154 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;
2155 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 );
2156 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;
2160 if ( dp->vtl->drawlabels )
2162 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2163 gint label_x, label_y;
2165 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2167 // Could this stored in the waypoint rather than recreating each pass?
2168 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2170 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2171 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2173 // Fallback if parse failure
2174 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2176 g_free ( wp_label_markup );
2178 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2179 label_x = x - width/2;
2180 if ( wp->symbol_pixbuf )
2181 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2183 label_y = y - dp->vtl->wp_size - height - 2;
2185 /* if highlight mode on, then draw background text in highlight colour */
2186 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2187 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2188 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2189 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2190 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2192 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2195 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2197 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2202 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2204 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2205 trw_layer_draw_waypoint ( id, wp, dp );
2209 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2211 static struct DrawingParams dp;
2212 g_assert ( l != NULL );
2214 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2216 if ( l->tracks_visible )
2217 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2219 if ( l->routes_visible )
2220 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2222 if (l->waypoints_visible)
2223 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2226 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2229 if ( vtl->track_bg_gc )
2231 g_object_unref ( vtl->track_bg_gc );
2232 vtl->track_bg_gc = NULL;
2234 if ( vtl->track_1color_gc )
2236 g_object_unref ( vtl->track_1color_gc );
2237 vtl->track_1color_gc = NULL;
2239 if ( vtl->current_track_gc )
2241 g_object_unref ( vtl->current_track_gc );
2242 vtl->current_track_gc = NULL;
2244 if ( vtl->current_track_newpoint_gc )
2246 g_object_unref ( vtl->current_track_newpoint_gc );
2247 vtl->current_track_newpoint_gc = NULL;
2250 if ( ! vtl->track_gc )
2252 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2253 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2254 g_array_free ( vtl->track_gc, TRUE );
2255 vtl->track_gc = NULL;
2258 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2260 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2261 gint width = vtl->line_thickness;
2263 if ( vtl->track_gc )
2264 trw_layer_free_track_gcs ( vtl );
2266 if ( vtl->track_bg_gc )
2267 g_object_unref ( vtl->track_bg_gc );
2268 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2270 // Ensure new track drawing heeds line thickness setting
2271 // however always have a minium of 2, as 1 pixel is really narrow
2272 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2274 if ( vtl->current_track_gc )
2275 g_object_unref ( vtl->current_track_gc );
2276 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2277 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2279 // 'newpoint' gc is exactly the same as the current track gc
2280 if ( vtl->current_track_newpoint_gc )
2281 g_object_unref ( vtl->current_track_newpoint_gc );
2282 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2283 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2285 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2287 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2288 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2290 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2291 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2292 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2294 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2296 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2299 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2301 VikTrwLayer *rv = trw_layer_new1 ( vp );
2302 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2304 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2305 /* early exit, as the rest is GUI related */
2309 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2310 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2312 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2313 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2315 trw_layer_new_track_gcs ( rv, vp );
2317 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2318 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2319 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2320 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2322 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2324 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2329 #define SMALL_ICON_SIZE 18
2331 * Can accept a null symbol, and may return null value
2333 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2335 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2336 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2337 // So needing a small icon for the treeview may need some resizing:
2338 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2339 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2343 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2345 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2347 GdkPixbuf *pixbuf = NULL;
2349 if ( track->has_color ) {
2350 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2351 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2352 // Here is some magic found to do the conversion
2353 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2354 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2355 ((track->color.green & 0xff00) << 8) |
2356 (track->color.blue & 0xff00);
2358 gdk_pixbuf_fill ( pixbuf, pixel );
2361 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 );
2364 g_object_unref (pixbuf);
2366 *new_iter = *((GtkTreeIter *) pass_along[1]);
2367 if ( track->is_route )
2368 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2370 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2372 if ( ! track->visible )
2373 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2376 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2378 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2380 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 );
2382 *new_iter = *((GtkTreeIter *) pass_along[1]);
2383 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2385 if ( ! wp->visible )
2386 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2389 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2391 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2394 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2396 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2399 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2401 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2404 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2407 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2409 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2410 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2412 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2414 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2417 if ( g_hash_table_size (vtl->routes) > 0 ) {
2418 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2420 pass_along[0] = &(vtl->routes_iter);
2421 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2423 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2425 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2428 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2429 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2431 pass_along[0] = &(vtl->waypoints_iter);
2432 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2434 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2436 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2441 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2445 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2446 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2447 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2448 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2450 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2452 return (t->visible ^= 1);
2456 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2458 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2460 return (t->visible ^= 1);
2464 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2466 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2468 return (t->visible ^= 1);
2477 * Return a property about tracks for this layer
2479 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2481 return vtl->line_thickness;
2484 // Structure to hold multiple track information for a layer
2493 * Build up layer multiple track information via updating the tooltip_tracks structure
2495 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2497 tt->length = tt->length + vik_track_get_length (tr);
2499 // Ensure times are available
2500 if ( tr->trackpoints &&
2501 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2502 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2505 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2506 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2508 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2509 // Hence initialize to the first 'proper' value
2510 if ( tt->start_time == 0 )
2511 tt->start_time = t1;
2512 if ( tt->end_time == 0 )
2515 // Update find the earliest / last times
2516 if ( t1 < tt->start_time )
2517 tt->start_time = t1;
2518 if ( t2 > tt->end_time )
2521 // Keep track of total time
2522 // there maybe gaps within a track (eg segments)
2523 // but this should be generally good enough for a simple indicator
2524 tt->duration = tt->duration + (int)(t2-t1);
2529 * Generate tooltip text for the layer.
2530 * This is relatively complicated as it considers information for
2531 * no tracks, a single track or multiple tracks
2532 * (which may or may not have timing information)
2534 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2545 static gchar tmp_buf[128];
2548 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2550 // Safety check - I think these should always be valid
2551 if ( vtl->tracks && vtl->waypoints ) {
2552 tooltip_tracks tt = { 0.0, 0, 0 };
2553 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2555 GDate* gdate_start = g_date_new ();
2556 g_date_set_time_t (gdate_start, tt.start_time);
2558 GDate* gdate_end = g_date_new ();
2559 g_date_set_time_t (gdate_end, tt.end_time);
2561 if ( g_date_compare (gdate_start, gdate_end) ) {
2562 // Dates differ so print range on separate line
2563 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2564 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2565 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2568 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2569 if ( tt.start_time != 0 )
2570 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2574 if ( tt.length > 0.0 ) {
2575 gdouble len_in_units;
2577 // Setup info dependent on distance units
2578 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2579 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2580 len_in_units = VIK_METERS_TO_MILES(tt.length);
2583 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2584 len_in_units = tt.length/1000.0;
2587 // Timing information if available
2589 if ( tt.duration > 0 ) {
2590 g_snprintf (tbuf1, sizeof(tbuf1),
2591 _(" in %d:%02d hrs:mins"),
2592 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2594 g_snprintf (tbuf2, sizeof(tbuf2),
2595 _("\n%sTotal Length %.1f %s%s"),
2596 tbuf3, len_in_units, tbuf4, tbuf1);
2599 // Put together all the elements to form compact tooltip text
2600 g_snprintf (tmp_buf, sizeof(tmp_buf),
2601 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2602 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2604 g_date_free (gdate_start);
2605 g_date_free (gdate_end);
2612 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2616 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2618 // Very simple tooltip - may expand detail in the future...
2619 static gchar tmp_buf[32];
2620 g_snprintf (tmp_buf, sizeof(tmp_buf),
2622 g_hash_table_size (l->tracks));
2626 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2628 // Very simple tooltip - may expand detail in the future...
2629 static gchar tmp_buf[32];
2630 g_snprintf (tmp_buf, sizeof(tmp_buf),
2632 g_hash_table_size (l->routes));
2637 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2638 // Same tooltip for a route
2639 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2642 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2643 tr = g_hash_table_lookup ( l->tracks, sublayer );
2645 tr = g_hash_table_lookup ( l->routes, sublayer );
2648 // Could be a better way of handling strings - but this works...
2649 gchar time_buf1[20];
2650 gchar time_buf2[20];
2651 time_buf1[0] = '\0';
2652 time_buf2[0] = '\0';
2653 static gchar tmp_buf[100];
2654 // Compact info: Short date eg (11/20/99), duration and length
2655 // Hopefully these are the things that are most useful and so promoted into the tooltip
2656 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2657 // %x The preferred date representation for the current locale without the time.
2658 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2659 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2660 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2662 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2665 // Get length and consider the appropriate distance units
2666 gdouble tr_len = vik_track_get_length(tr);
2667 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2668 switch (dist_units) {
2669 case VIK_UNITS_DISTANCE_KILOMETRES:
2670 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2672 case VIK_UNITS_DISTANCE_MILES:
2673 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2682 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2684 // Very simple tooltip - may expand detail in the future...
2685 static gchar tmp_buf[32];
2686 g_snprintf (tmp_buf, sizeof(tmp_buf),
2688 g_hash_table_size (l->waypoints));
2692 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2694 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2695 // NB It's OK to return NULL
2700 return w->description;
2709 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2712 * set_statusbar_msg_info_trkpt:
2714 * Function to show track point information on the statusbar
2715 * Items displayed is controlled by the settings format code
2717 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2719 gchar *statusbar_format_code = NULL;
2720 gboolean need2free = FALSE;
2721 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2722 // Otherwise use default
2723 statusbar_format_code = g_strdup ( "KEATDN" );
2727 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2728 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2732 g_free ( statusbar_format_code );
2736 * Function to show basic waypoint information on the statusbar
2738 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2741 switch (a_vik_get_units_height ()) {
2742 case VIK_UNITS_HEIGHT_FEET:
2743 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2746 //VIK_UNITS_HEIGHT_METRES:
2747 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2751 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2752 // one can easily use the current pointer position to see this if needed
2753 gchar *lat = NULL, *lon = NULL;
2754 static struct LatLon ll;
2755 vik_coord_to_latlon (&(wpt->coord), &ll);
2756 a_coords_latlon_to_string ( &ll, &lat, &lon );
2758 // Combine parts to make overall message
2761 // Add comment if available
2762 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2764 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2765 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2772 * General layer selection function, find out which bit is selected and take appropriate action
2774 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2777 l->current_wp = NULL;
2778 l->current_wp_id = NULL;
2779 trw_layer_cancel_current_tp ( l, FALSE );
2782 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2786 case VIK_TREEVIEW_TYPE_LAYER:
2788 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2789 /* Mark for redraw */
2794 case VIK_TREEVIEW_TYPE_SUBLAYER:
2798 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2800 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2801 /* Mark for redraw */
2805 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2807 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2808 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2809 /* Mark for redraw */
2813 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2815 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2816 /* Mark for redraw */
2820 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2822 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2823 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2824 /* Mark for redraw */
2828 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2830 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2831 /* Mark for redraw */
2835 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2837 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2839 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2840 // Show some waypoint info
2841 set_statusbar_msg_info_wpt ( l, wpt );
2842 /* Mark for redraw */
2849 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2858 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2863 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2868 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2873 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2875 return l->waypoints;
2878 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2880 return vtl->tracks_iters;
2883 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2885 return vtl->routes_iters;
2888 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2890 return vtl->waypoints;
2893 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2895 return ! ( g_hash_table_size ( vtl->tracks ) ||
2896 g_hash_table_size ( vtl->routes ) ||
2897 g_hash_table_size ( vtl->waypoints ) );
2900 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2902 return vtl->tracks_visible;
2905 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2907 return vtl->routes_visible;
2910 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2912 return vtl->waypoints_visible;
2916 * ATM use a case sensitive find
2917 * Finds the first one
2919 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2921 if ( wp && wp->name )
2922 if ( ! strcmp ( wp->name, name ) )
2928 * Get waypoint by name - not guaranteed to be unique
2929 * Finds the first one
2931 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2933 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2937 * ATM use a case sensitive find
2938 * Finds the first one
2940 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2942 if ( trk && trk->name )
2943 if ( ! strcmp ( trk->name, name ) )
2949 * Get track by name - not guaranteed to be unique
2950 * Finds the first one
2952 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2954 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2958 * Get route by name - not guaranteed to be unique
2959 * Finds the first one
2961 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2963 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2966 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2968 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2969 maxmin[0].lat = trk->bbox.north;
2970 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2971 maxmin[1].lat = trk->bbox.south;
2972 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2973 maxmin[0].lon = trk->bbox.east;
2974 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2975 maxmin[1].lon = trk->bbox.west;
2978 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2980 // Continually reuse maxmin to find the latest maximum and minimum values
2981 // First set to waypoints bounds
2982 maxmin[0].lat = vtl->waypoints_bbox.north;
2983 maxmin[1].lat = vtl->waypoints_bbox.south;
2984 maxmin[0].lon = vtl->waypoints_bbox.east;
2985 maxmin[1].lon = vtl->waypoints_bbox.west;
2986 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2987 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2990 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2992 /* 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... */
2993 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2994 trw_layer_find_maxmin (vtl, maxmin);
2995 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2999 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3000 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3005 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
3008 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
3009 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
3011 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3014 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3016 /* First set the center [in case previously viewing from elsewhere] */
3017 /* Then loop through zoom levels until provided positions are in view */
3018 /* This method is not particularly fast - but should work well enough */
3019 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3021 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3022 vik_viewport_set_center_coord ( vvp, &coord );
3024 /* Convert into definite 'smallest' and 'largest' positions */
3025 struct LatLon minmin;
3026 if ( maxmin[0].lat < maxmin[1].lat )
3027 minmin.lat = maxmin[0].lat;
3029 minmin.lat = maxmin[1].lat;
3031 struct LatLon maxmax;
3032 if ( maxmin[0].lon > maxmin[1].lon )
3033 maxmax.lon = maxmin[0].lon;
3035 maxmax.lon = maxmin[1].lon;
3037 /* Never zoom in too far - generally not that useful, as too close ! */
3038 /* Always recalculate the 'best' zoom level */
3040 vik_viewport_set_zoom ( vvp, zoom );
3042 gdouble min_lat, max_lat, min_lon, max_lon;
3043 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3044 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3045 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3046 /* NB I think the logic used in this test to determine if the bounds is within view
3047 fails if track goes across 180 degrees longitude.
3048 Hopefully that situation is not too common...
3049 Mind you viking doesn't really do edge locations to well anyway */
3050 if ( min_lat < minmin.lat &&
3051 max_lat > minmin.lat &&
3052 min_lon < maxmax.lon &&
3053 max_lon > maxmax.lon )
3054 /* Found within zoom level */
3059 vik_viewport_set_zoom ( vvp, zoom );
3063 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3065 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3066 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3067 trw_layer_find_maxmin (vtl, maxmin);
3068 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3071 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3076 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
3078 if ( vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(layer_and_vlp[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(layer_and_vlp[1])) ) ) {
3079 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3082 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3085 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3087 GtkWidget *file_selector;
3089 gboolean failed = FALSE;
3090 file_selector = gtk_file_chooser_dialog_new (title,
3092 GTK_FILE_CHOOSER_ACTION_SAVE,
3093 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3094 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3096 gchar *cwd = g_get_current_dir();
3098 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3102 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3104 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3106 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3107 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ||
3108 a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3110 gtk_widget_hide ( file_selector );
3111 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3112 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3113 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3117 gtk_widget_destroy ( file_selector );
3119 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3122 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3124 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3127 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3129 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3132 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3134 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3135 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3136 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3137 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3139 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3141 g_free ( auto_save_name );
3144 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3146 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3147 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3148 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3149 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3151 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3153 g_free ( auto_save_name );
3157 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3160 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3162 gchar *name_used = NULL;
3165 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3166 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3167 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3168 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3170 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3174 gchar *quoted_file = g_shell_quote ( name_used );
3175 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3176 g_free ( quoted_file );
3177 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3179 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3180 g_error_free ( err );
3184 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3185 //g_remove ( name_used );
3186 // Perhaps should be deleted when the program ends?
3187 // For now leave it to the user to delete it / use system temp cleanup methods.
3188 g_free ( name_used );
3192 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3194 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3197 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3199 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3202 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3204 gpointer layer_and_vlp[2];
3205 layer_and_vlp[0] = pass_along[0];
3206 layer_and_vlp[1] = pass_along[1];
3208 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3210 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3211 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3213 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3215 if ( !trk || !trk->name )
3218 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3219 gchar *auto_save_name = g_strdup ( trk->name );
3220 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3221 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3223 gchar *label = NULL;
3224 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3225 label = _("Export Route as GPX");
3227 label = _("Export Track as GPX");
3228 trw_layer_export ( layer_and_vlp, label, auto_save_name, trk, FILE_TYPE_GPX );
3230 g_free ( auto_save_name );
3233 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3235 wpu_udata *user_data = udata;
3236 if ( wp == user_data->wp ) {
3237 user_data->uuid = id;
3243 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3245 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3246 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3247 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3249 GTK_RESPONSE_REJECT,
3251 GTK_RESPONSE_ACCEPT,
3254 GtkWidget *label, *entry;
3255 label = gtk_label_new(_("Waypoint Name:"));
3256 entry = gtk_entry_new();
3258 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3259 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3260 gtk_widget_show_all ( label );
3261 gtk_widget_show_all ( entry );
3263 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3265 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3267 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3268 // Find *first* wp with the given name
3269 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3272 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3275 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3276 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3278 // Find and select on the side panel
3283 // Hmmm, want key of it
3284 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3286 if ( wpf && udata.uuid ) {
3287 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3288 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3297 gtk_widget_destroy ( dia );
3300 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3302 gchar *default_name = highest_wp_number_get(vtl);
3303 VikWaypoint *wp = vik_waypoint_new();
3304 gchar *returned_name;
3306 wp->coord = *def_coord;
3308 // Attempt to auto set height if DEM data is available
3309 vik_waypoint_apply_dem_data ( wp, TRUE );
3311 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3313 if ( returned_name )
3316 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3317 g_free (default_name);
3318 g_free (returned_name);
3321 g_free (default_name);
3322 vik_waypoint_free(wp);
3326 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3328 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3329 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3330 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3331 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3332 VikViewport *vvp = vik_window_viewport(vw);
3334 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3335 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3336 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3337 trw_layer_calculate_bounds_waypoints ( vtl );
3338 vik_layers_panel_emit_update ( vlp );
3341 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3343 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3344 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3345 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3347 trw_layer_find_maxmin (vtl, maxmin);
3348 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3349 trw_layer_calculate_bounds_waypoints ( vtl );
3350 vik_layers_panel_emit_update ( vlp );
3353 #ifdef VIK_CONFIG_GEOTAG
3354 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3356 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3358 // Update directly - not changing the mtime
3359 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3362 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3364 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3367 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3371 * Use code in separate file for this feature as reasonably complex
3373 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3375 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3376 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3377 // Unset so can be reverified later if necessary
3378 vtl->has_verified_thumbnails = FALSE;
3380 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3386 static void trw_layer_geotagging_waypoint ( gpointer pass_along[6] )
3388 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3389 VikWaypoint *wpt = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3391 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3397 static void trw_layer_geotagging ( gpointer lav[2] )
3399 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3400 // Unset so can be reverified later if necessary
3401 vtl->has_verified_thumbnails = FALSE;
3403 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3410 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3413 * Acquire into this TRW Layer straight from GPS Device
3415 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3417 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3418 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3419 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3420 VikViewport *vvp = vik_window_viewport(vw);
3422 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3423 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3427 * Acquire into this TRW Layer from Directions
3429 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3431 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3432 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3433 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3434 VikViewport *vvp = vik_window_viewport(vw);
3436 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3440 * Acquire into this TRW Layer from an entered URL
3442 static void trw_layer_acquire_url_cb ( gpointer lav[2] )
3444 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3445 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3446 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3447 VikViewport *vvp = vik_window_viewport(vw);
3449 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3450 a_acquire ( vw, vlp, vvp, &vik_datasource_url_interface, NULL, NULL );
3453 #ifdef VIK_CONFIG_OPENSTREETMAP
3455 * Acquire into this TRW Layer from OSM
3457 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3459 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3460 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3461 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3462 VikViewport *vvp = vik_window_viewport(vw);
3464 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3468 * Acquire into this TRW Layer from OSM for 'My' Traces
3470 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3472 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3473 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3474 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3475 VikViewport *vvp = vik_window_viewport(vw);
3477 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3481 #ifdef VIK_CONFIG_GEOCACHES
3483 * Acquire into this TRW Layer from Geocaching.com
3485 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3487 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3488 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3489 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3490 VikViewport *vvp = vik_window_viewport(vw);
3492 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3496 #ifdef VIK_CONFIG_GEOTAG
3498 * Acquire into this TRW Layer from images
3500 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3502 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3503 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3504 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3505 VikViewport *vvp = vik_window_viewport(vw);
3507 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3508 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3510 // Reverify thumbnails as they may have changed
3511 vtl->has_verified_thumbnails = FALSE;
3512 trw_layer_verify_thumbnails ( vtl, NULL );
3516 static void trw_layer_gps_upload ( gpointer lav[2] )
3518 gpointer pass_along[6];
3519 pass_along[0] = lav[0];
3520 pass_along[1] = lav[1];
3521 pass_along[2] = NULL; // No track - operate on the layer
3522 pass_along[3] = NULL;
3523 pass_along[4] = NULL;
3524 pass_along[5] = NULL;
3526 trw_layer_gps_upload_any ( pass_along );
3530 * If pass_along[3] is defined that this will upload just that track
3532 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3534 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3535 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3537 // May not actually get a track here as pass_along[2&3] can be null
3538 VikTrack *track = NULL;
3539 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3540 gboolean xfer_all = FALSE;
3542 if ( pass_along[2] ) {
3544 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3545 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3548 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3549 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3552 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3555 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3559 else if ( !pass_along[4] )
3560 xfer_all = TRUE; // i.e. whole layer
3562 if (track && !track->visible) {
3563 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3567 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3568 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3569 GTK_DIALOG_DESTROY_WITH_PARENT,
3571 GTK_RESPONSE_ACCEPT,
3573 GTK_RESPONSE_REJECT,
3576 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3577 GtkWidget *response_w = NULL;
3578 #if GTK_CHECK_VERSION (2, 20, 0)
3579 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3583 gtk_widget_grab_focus ( response_w );
3585 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3587 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3588 datasource_gps_clean_up ( dgs );
3589 gtk_widget_destroy ( dialog );
3593 // Get info from reused datasource dialog widgets
3594 gchar* protocol = datasource_gps_get_protocol ( dgs );
3595 gchar* port = datasource_gps_get_descriptor ( dgs );
3596 // NB don't free the above strings as they're references to values held elsewhere
3597 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3598 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3599 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3600 gboolean turn_off = datasource_gps_get_off ( dgs );
3602 gtk_widget_destroy ( dialog );
3604 // When called from the viewport - work the corresponding layerspanel:
3606 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3609 // Apply settings to transfer to the GPS device
3616 vik_layers_panel_get_viewport (vlp),
3625 * Acquire into this TRW Layer from any GPS Babel supported file
3627 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3629 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3630 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3631 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3632 VikViewport *vvp = vik_window_viewport(vw);
3634 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3637 static void trw_layer_new_wp ( gpointer lav[2] )
3639 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3640 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3641 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3642 instead return true if you want to update. */
3643 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 ) {
3644 trw_layer_calculate_bounds_waypoints ( vtl );
3645 vik_layers_panel_emit_update ( vlp );
3649 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3651 vtl->current_track = vik_track_new();
3652 vik_track_set_defaults ( vtl->current_track );
3653 vtl->current_track->visible = TRUE;
3654 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3655 // Create track with the preferred colour from the layer properties
3656 vtl->current_track->color = vtl->track_color;
3658 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3659 vtl->current_track->has_color = TRUE;
3660 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3663 static void trw_layer_new_track ( gpointer lav[2] )
3665 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3667 if ( ! vtl->current_track ) {
3668 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3669 new_track_create_common ( vtl, name );
3672 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3676 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3678 vtl->current_track = vik_track_new();
3679 vik_track_set_defaults ( vtl->current_track );
3680 vtl->current_track->visible = TRUE;
3681 vtl->current_track->is_route = TRUE;
3682 // By default make all routes red
3683 vtl->current_track->has_color = TRUE;
3684 gdk_color_parse ( "red", &vtl->current_track->color );
3685 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3688 static void trw_layer_new_route ( gpointer lav[2] )
3690 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3692 if ( ! vtl->current_track ) {
3693 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3694 new_route_create_common ( vtl, name );
3696 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3700 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3702 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3703 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3705 if ( g_hash_table_size (vtl->routes) > 0 ) {
3706 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3707 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3708 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3709 vik_layers_panel_emit_update ( vlp );
3714 static void trw_layer_finish_track ( gpointer lav[2] )
3716 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3717 vtl->current_track = NULL;
3718 vik_layer_emit_update ( VIK_LAYER(vtl) );
3721 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3723 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3724 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3726 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3727 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3728 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3729 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3730 vik_layers_panel_emit_update ( vlp );
3734 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3736 /* NB do not care if wp is visible or not */
3737 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3740 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3742 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3743 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3745 /* Only 1 waypoint - jump straight to it */
3746 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3747 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3748 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3750 /* If at least 2 waypoints - find center and then zoom to fit */
3751 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3753 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3754 maxmin[0].lat = vtl->waypoints_bbox.north;
3755 maxmin[1].lat = vtl->waypoints_bbox.south;
3756 maxmin[0].lon = vtl->waypoints_bbox.east;
3757 maxmin[1].lon = vtl->waypoints_bbox.west;
3758 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3761 vik_layers_panel_emit_update ( vlp );
3764 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3766 static gpointer pass_along[2];
3768 GtkWidget *export_submenu;
3769 pass_along[0] = vtl;
3770 pass_along[1] = vlp;
3772 item = gtk_menu_item_new();
3773 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3774 gtk_widget_show ( item );
3776 if ( vtl->current_track ) {
3777 if ( vtl->current_track->is_route )
3778 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3780 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3782 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3783 gtk_widget_show ( item );
3786 item = gtk_menu_item_new ();
3787 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3788 gtk_widget_show ( item );
3791 /* Now with icons */
3792 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3793 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3795 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3796 gtk_widget_show ( item );
3798 GtkWidget *view_submenu = gtk_menu_new();
3799 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3800 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3801 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3802 gtk_widget_show ( item );
3803 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3805 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3806 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3807 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3808 gtk_widget_show ( item );
3810 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3811 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3812 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3813 gtk_widget_show ( item );
3815 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3817 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3818 gtk_widget_show ( item );
3820 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3823 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3824 gtk_widget_show ( item );
3826 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3828 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3829 gtk_widget_show ( item );
3831 export_submenu = gtk_menu_new ();
3832 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3833 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3835 gtk_widget_show ( item );
3836 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3838 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3839 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3840 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3841 gtk_widget_show ( item );
3843 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3844 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3845 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3846 gtk_widget_show ( item );
3848 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3849 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3850 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3851 gtk_widget_show ( item );
3853 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3854 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3855 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3856 gtk_widget_show ( item );
3858 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3859 item = gtk_menu_item_new_with_mnemonic ( external1 );
3860 g_free ( external1 );
3861 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3862 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3863 gtk_widget_show ( item );
3865 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3866 item = gtk_menu_item_new_with_mnemonic ( external2 );
3867 g_free ( external2 );
3868 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3869 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3870 gtk_widget_show ( item );
3872 GtkWidget *new_submenu = gtk_menu_new();
3873 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3874 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3875 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3876 gtk_widget_show(item);
3877 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3879 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3880 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3882 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3883 gtk_widget_show ( item );
3885 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3888 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3889 gtk_widget_show ( item );
3890 // Make it available only when a new track *not* already in progress
3891 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3893 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3896 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3897 gtk_widget_show ( item );
3898 // Make it available only when a new track *not* already in progress
3899 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3901 #ifdef VIK_CONFIG_GEOTAG
3902 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3903 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3904 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3905 gtk_widget_show ( item );
3908 GtkWidget *acquire_submenu = gtk_menu_new ();
3909 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3910 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3911 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3912 gtk_widget_show ( item );
3913 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3915 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3916 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3917 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3918 gtk_widget_show ( item );
3920 /* FIXME: only add menu when at least a routing engine has support for Directions */
3921 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3923 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3924 gtk_widget_show ( item );
3926 #ifdef VIK_CONFIG_OPENSTREETMAP
3927 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3929 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3930 gtk_widget_show ( item );
3932 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3934 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3935 gtk_widget_show ( item );
3938 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
3939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
3940 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3941 gtk_widget_show ( item );
3943 #ifdef VIK_CONFIG_GEONAMES
3944 GtkWidget *wikipedia_submenu = gtk_menu_new();
3945 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3946 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3947 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3948 gtk_widget_show(item);
3949 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3951 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3952 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3954 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3955 gtk_widget_show ( item );
3957 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3958 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3960 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3961 gtk_widget_show ( item );
3964 #ifdef VIK_CONFIG_GEOCACHES
3965 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3966 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3967 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3968 gtk_widget_show ( item );
3971 #ifdef VIK_CONFIG_GEOTAG
3972 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3974 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3975 gtk_widget_show ( item );
3978 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3980 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3981 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
3982 gtk_widget_show ( item );
3984 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3986 GtkWidget *upload_submenu = gtk_menu_new ();
3987 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3988 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3989 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3990 gtk_widget_show ( item );
3991 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3993 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3994 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3996 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3997 gtk_widget_show ( item );
3999 #ifdef VIK_CONFIG_OPENSTREETMAP
4000 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
4003 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4004 gtk_widget_show ( item );
4007 GtkWidget *delete_submenu = gtk_menu_new ();
4008 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4009 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4010 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4011 gtk_widget_show ( item );
4012 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4014 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4016 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4017 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4018 gtk_widget_show ( item );
4020 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4023 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4024 gtk_widget_show ( item );
4026 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4029 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4030 gtk_widget_show ( item );
4032 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4035 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4036 gtk_widget_show ( item );
4038 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4041 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4042 gtk_widget_show ( item );
4044 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4045 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4046 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4047 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4048 gtk_widget_show ( item );
4050 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4051 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4053 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4054 gtk_widget_show ( item );
4057 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4058 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4060 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4061 gtk_widget_show ( item );
4064 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4065 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4067 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4068 gtk_widget_show ( item );
4069 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4071 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4074 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4075 gtk_widget_show ( item );
4076 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4079 // Fake Waypoint UUIDs vi simple increasing integer
4080 static guint wp_uuid = 0;
4082 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4086 vik_waypoint_set_name (wp, name);
4088 if ( VIK_LAYER(vtl)->realized )
4090 // Do we need to create the sublayer:
4091 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4092 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4095 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4097 // Visibility column always needed for waypoints
4098 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 );
4100 // Actual setting of visibility dependent on the waypoint
4101 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4103 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4105 // Sort now as post_read is not called on a realized waypoint
4106 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4109 highest_wp_number_add_wp(vtl, name);
4110 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4114 // Fake Track UUIDs vi simple increasing integer
4115 static guint tr_uuid = 0;
4117 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4121 vik_track_set_name (t, name);
4123 if ( VIK_LAYER(vtl)->realized )
4125 // Do we need to create the sublayer:
4126 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4127 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4130 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4131 // Visibility column always needed for tracks
4132 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 );
4134 // Actual setting of visibility dependent on the track
4135 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4137 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4139 // Sort now as post_read is not called on a realized track
4140 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4143 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4145 trw_layer_update_treeview ( vtl, t );
4148 // Fake Route UUIDs vi simple increasing integer
4149 static guint rt_uuid = 0;
4151 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4155 vik_track_set_name (t, name);
4157 if ( VIK_LAYER(vtl)->realized )
4159 // Do we need to create the sublayer:
4160 if ( g_hash_table_size (vtl->routes) == 0 ) {
4161 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4164 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4165 // Visibility column always needed for routes
4166 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 );
4167 // Actual setting of visibility dependent on the route
4168 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4170 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4172 // Sort now as post_read is not called on a realized route
4173 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4176 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4178 trw_layer_update_treeview ( vtl, t );
4181 /* to be called whenever a track has been deleted or may have been changed. */
4182 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4184 if (vtl->current_tp_track == trk )
4185 trw_layer_cancel_current_tp ( vtl, FALSE );
4189 * Normally this is done to due the waypoint size preference having changed
4191 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4193 GHashTableIter iter;
4194 gpointer key, value;
4197 g_hash_table_iter_init ( &iter, vtl->waypoints );
4198 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4199 VikWaypoint *wp = VIK_WAYPOINT(value);
4201 // Reapply symbol setting to update the pixbuf
4202 gchar *tmp_symbol = g_strdup ( wp->symbol );
4203 vik_waypoint_set_symbol ( wp, tmp_symbol );
4204 g_free ( tmp_symbol );
4210 * trw_layer_new_unique_sublayer_name:
4212 * Allocates a unique new name
4214 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4217 gchar *newname = g_strdup(name);
4222 switch ( sublayer_type ) {
4223 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4224 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4226 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4227 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4230 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4233 // If found a name already in use try adding 1 to it and we try again
4235 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4237 newname = new_newname;
4240 } while ( id != NULL);
4245 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4247 // No more uniqueness of name forced when loading from a file
4248 // This now makes this function a little redunant as we just flow the parameters through
4249 vik_trw_layer_add_waypoint ( vtl, name, wp );
4252 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4254 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4255 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4256 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4257 vik_track_free ( tr );
4258 vtl->route_finder_append = FALSE; /* this means we have added it */
4261 // No more uniqueness of name forced when loading from a file
4263 vik_trw_layer_add_route ( vtl, name, tr );
4265 vik_trw_layer_add_track ( vtl, name, tr );
4267 if ( vtl->route_finder_check_added_track ) {
4268 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4269 vtl->route_finder_added_track = tr;
4274 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4276 *l = g_list_append(*l, id);
4280 * Move an item from one TRW layer to another TRW layer
4282 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4284 // TODO reconsider strategy when moving within layer (if anything...)
4285 gboolean rename = ( vtl_src != vtl_dest );
4289 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4290 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4294 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4296 newname = g_strdup ( trk->name );
4298 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4299 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4301 vik_trw_layer_delete_track ( vtl_src, trk );
4304 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4305 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4309 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4311 newname = g_strdup ( trk->name );
4313 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4314 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4316 vik_trw_layer_delete_route ( vtl_src, trk );
4319 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4320 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4324 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4326 newname = g_strdup ( wp->name );
4328 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4329 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4331 trw_layer_delete_waypoint ( vtl_src, wp );
4333 // Recalculate bounds even if not renamed as maybe dragged between layers
4334 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4335 trw_layer_calculate_bounds_waypoints ( vtl_src );
4339 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4341 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4342 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4344 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4345 GList *items = NULL;
4348 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4349 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4351 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4352 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4354 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4355 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4360 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4361 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4362 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4363 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4365 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4372 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4373 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4377 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4379 trku_udata *user_data = udata;
4380 if ( trk == user_data->trk ) {
4381 user_data->uuid = id;
4387 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4389 gboolean was_visible = FALSE;
4391 if ( trk && trk->name ) {
4393 if ( trk == vtl->current_track ) {
4394 vtl->current_track = NULL;
4395 vtl->current_tp_track = NULL;
4396 vtl->current_tp_id = NULL;
4397 vtl->moving_tp = FALSE;
4400 was_visible = trk->visible;
4402 if ( trk == vtl->route_finder_current_track )
4403 vtl->route_finder_current_track = NULL;
4405 if ( trk == vtl->route_finder_added_track )
4406 vtl->route_finder_added_track = NULL;
4412 // Hmmm, want key of it
4413 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4415 if ( trkf && udata.uuid ) {
4416 /* could be current_tp, so we have to check */
4417 trw_layer_cancel_tps_of_track ( vtl, trk );
4419 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4422 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4423 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4424 g_hash_table_remove ( vtl->tracks, udata.uuid );
4426 // If last sublayer, then remove sublayer container
4427 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4428 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4436 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4438 gboolean was_visible = FALSE;
4440 if ( trk && trk->name ) {
4442 if ( trk == vtl->current_track ) {
4443 vtl->current_track = NULL;
4444 vtl->current_tp_track = NULL;
4445 vtl->current_tp_id = NULL;
4446 vtl->moving_tp = FALSE;
4449 was_visible = trk->visible;
4451 if ( trk == vtl->route_finder_current_track )
4452 vtl->route_finder_current_track = NULL;
4454 if ( trk == vtl->route_finder_added_track )
4455 vtl->route_finder_added_track = NULL;
4461 // Hmmm, want key of it
4462 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4464 if ( trkf && udata.uuid ) {
4465 /* could be current_tp, so we have to check */
4466 trw_layer_cancel_tps_of_track ( vtl, trk );
4468 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4471 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4472 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4473 g_hash_table_remove ( vtl->routes, udata.uuid );
4475 // If last sublayer, then remove sublayer container
4476 if ( g_hash_table_size (vtl->routes) == 0 ) {
4477 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4485 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4487 gboolean was_visible = FALSE;
4489 if ( wp && wp->name ) {
4491 if ( wp == vtl->current_wp ) {
4492 vtl->current_wp = NULL;
4493 vtl->current_wp_id = NULL;
4494 vtl->moving_wp = FALSE;
4497 was_visible = wp->visible;
4503 // Hmmm, want key of it
4504 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4506 if ( wpf && udata.uuid ) {
4507 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4510 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4511 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4513 highest_wp_number_remove_wp(vtl, wp->name);
4514 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4516 // If last sublayer, then remove sublayer container
4517 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4518 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4528 // Only for temporary use by trw_layer_delete_waypoint_by_name
4529 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4531 wpu_udata *user_data = udata;
4532 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4533 user_data->uuid = id;
4540 * Delete a waypoint by the given name
4541 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4542 * as there be multiple waypoints with the same name
4544 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4547 // Fake a waypoint with the given name
4548 udata.wp = vik_waypoint_new ();
4549 vik_waypoint_set_name (udata.wp, name);
4550 // Currently only the name is used in this waypoint find function
4553 // Hmmm, want key of it
4554 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4556 vik_waypoint_free (udata.wp);
4558 if ( wpf && udata.uuid )
4559 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4565 VikTrack *trk; // input
4566 gpointer uuid; // output
4569 // Only for temporary use by trw_layer_delete_track_by_name
4570 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4572 tpu_udata *user_data = udata;
4573 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4574 user_data->uuid = id;
4581 * Delete a track by the given name
4582 * NOTE: ATM this will delete the first encountered Track with the specified name
4583 * as there may be multiple tracks with the same name within the specified hash table
4585 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4588 // Fake a track with the given name
4589 udata.trk = vik_track_new ();
4590 vik_track_set_name (udata.trk, name);
4591 // Currently only the name is used in this waypoint find function
4594 // Hmmm, want key of it
4595 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4597 vik_track_free (udata.trk);
4599 if ( trkf && udata.uuid ) {
4600 // This could be a little better written...
4601 if ( vtl->tracks == ht_tracks )
4602 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4603 if ( vtl->routes == ht_tracks )
4604 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4611 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4613 vik_treeview_item_delete (vt, it );
4616 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4619 vtl->current_track = NULL;
4620 vtl->route_finder_current_track = NULL;
4621 vtl->route_finder_added_track = NULL;
4622 if (vtl->current_tp_track)
4623 trw_layer_cancel_current_tp(vtl, FALSE);
4625 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4626 g_hash_table_remove_all(vtl->routes_iters);
4627 g_hash_table_remove_all(vtl->routes);
4629 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4631 vik_layer_emit_update ( VIK_LAYER(vtl) );
4634 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4637 vtl->current_track = NULL;
4638 vtl->route_finder_current_track = NULL;
4639 vtl->route_finder_added_track = NULL;
4640 if (vtl->current_tp_track)
4641 trw_layer_cancel_current_tp(vtl, FALSE);
4643 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4644 g_hash_table_remove_all(vtl->tracks_iters);
4645 g_hash_table_remove_all(vtl->tracks);
4647 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4649 vik_layer_emit_update ( VIK_LAYER(vtl) );
4652 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4654 vtl->current_wp = NULL;
4655 vtl->current_wp_id = NULL;
4656 vtl->moving_wp = FALSE;
4658 highest_wp_number_reset(vtl);
4660 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4661 g_hash_table_remove_all(vtl->waypoints_iters);
4662 g_hash_table_remove_all(vtl->waypoints);
4664 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4666 vik_layer_emit_update ( VIK_LAYER(vtl) );
4669 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4671 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4672 // Get confirmation from the user
4673 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4674 _("Are you sure you want to delete all tracks in %s?"),
4675 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4676 vik_trw_layer_delete_all_tracks (vtl);
4679 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4681 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4682 // Get confirmation from the user
4683 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4684 _("Are you sure you want to delete all routes in %s?"),
4685 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4686 vik_trw_layer_delete_all_routes (vtl);
4689 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4691 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4692 // Get confirmation from the user
4693 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4694 _("Are you sure you want to delete all waypoints in %s?"),
4695 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4696 vik_trw_layer_delete_all_waypoints (vtl);
4699 static void trw_layer_delete_item ( gpointer pass_along[6] )
4701 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4702 gboolean was_visible = FALSE;
4703 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4705 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4706 if ( wp && wp->name ) {
4707 if ( GPOINTER_TO_INT ( pass_along[4]) )
4708 // Get confirmation from the user
4709 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4710 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4711 _("Are you sure you want to delete the waypoint \"%s\"?"),
4714 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4717 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4719 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4720 if ( trk && trk->name ) {
4721 if ( GPOINTER_TO_INT ( pass_along[4]) )
4722 // Get confirmation from the user
4723 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4724 _("Are you sure you want to delete the track \"%s\"?"),
4727 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4732 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4733 if ( trk && trk->name ) {
4734 if ( GPOINTER_TO_INT ( pass_along[4]) )
4735 // Get confirmation from the user
4736 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4737 _("Are you sure you want to delete the route \"%s\"?"),
4740 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4744 vik_layer_emit_update ( VIK_LAYER(vtl) );
4748 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4750 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4752 vik_waypoint_set_name ( wp, new_name );
4754 // Now update the treeview as well
4759 // Need key of it for treeview update
4760 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4762 if ( wpf && udataU.uuid ) {
4763 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4766 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4767 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4773 * Maintain icon of waypoint in the treeview
4775 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4777 // update the treeview
4782 // Need key of it for treeview update
4783 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4785 if ( wpf && udataU.uuid ) {
4786 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4789 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4794 static void trw_layer_properties_item ( gpointer pass_along[7] )
4796 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4797 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4799 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4801 if ( wp && wp->name )
4803 gboolean updated = FALSE;
4804 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4806 trw_layer_waypoint_rename ( vtl, wp, new_name );
4808 if ( updated && pass_along[6] )
4809 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4811 if ( updated && VIK_LAYER(vtl)->visible )
4812 vik_layer_emit_update ( VIK_LAYER(vtl) );
4818 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4819 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4821 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4823 if ( tr && tr->name )
4825 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4828 pass_along[1], /* vlp */
4829 pass_along[5], /* vvp */
4836 * trw_layer_track_statistics:
4838 * Show track statistics.
4839 * ATM jump to the stats page in the properties
4840 * TODO: consider separating the stats into an individual dialog?
4842 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4844 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4846 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4847 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4849 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4851 if ( trk && trk->name ) {
4852 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4855 pass_along[1], // vlp
4856 pass_along[5], // vvp
4862 * Update the treeview of the track id - primarily to update the icon
4864 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4870 gpointer *trkf = NULL;
4871 if ( trk->is_route )
4872 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4874 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4876 if ( trkf && udata.uuid ) {
4878 GtkTreeIter *iter = NULL;
4879 if ( trk->is_route )
4880 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4882 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4885 // TODO: Make this a function
4886 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4887 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4888 ((trk->color.green & 0xff00) << 8) |
4889 (trk->color.blue & 0xff00);
4890 gdk_pixbuf_fill ( pixbuf, pixel );
4891 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4892 g_object_unref (pixbuf);
4899 Parameter 1 -> VikLayersPanel
4900 Parameter 2 -> VikLayer
4901 Parameter 3 -> VikViewport
4903 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4906 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4907 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4910 /* since vlp not set, vl & vvp should be valid instead! */
4912 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4913 vik_layer_emit_update ( VIK_LAYER(vl) );
4918 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4920 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4922 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4923 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4925 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4927 if ( track && track->trackpoints )
4928 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4931 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4933 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4935 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4936 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4938 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4940 if ( track && track->trackpoints )
4942 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4944 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4945 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4946 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4947 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4948 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4952 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4954 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4956 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4957 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4959 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4964 // Converting a track to a route can be a bit more complicated,
4965 // so give a chance to change our minds:
4966 if ( !trk->is_route &&
4967 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4968 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4970 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4971 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4976 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4979 trk_copy->is_route = !trk_copy->is_route;
4981 // ATM can't set name to self - so must create temporary copy
4982 gchar *name = g_strdup ( trk_copy->name );
4984 // Delete old one and then add new one
4985 if ( trk->is_route ) {
4986 vik_trw_layer_delete_route ( vtl, trk );
4987 vik_trw_layer_add_track ( vtl, name, trk_copy );
4990 // Extra route conversion bits...
4991 vik_track_merge_segments ( trk_copy );
4992 vik_track_to_routepoints ( trk_copy );
4994 vik_trw_layer_delete_track ( vtl, trk );
4995 vik_trw_layer_add_route ( vtl, name, trk_copy );
4999 // Update in case color of track / route changes when moving between sublayers
5000 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5003 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
5005 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5007 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5008 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5010 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5013 vik_track_anonymize_times ( track );
5016 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
5018 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5020 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5021 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5023 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5028 vtl->current_track = track;
5029 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);
5031 if ( track->trackpoints )
5032 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
5036 * extend a track using route finder
5038 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
5040 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5041 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
5044 if ( !track->trackpoints )
5046 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
5048 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5049 vtl->route_finder_coord = last_coord;
5050 vtl->route_finder_current_track = track;
5051 vtl->route_finder_started = TRUE;
5053 if ( track->trackpoints )
5054 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
5061 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5063 // If have a vlp then perform a basic test to see if any DEM info available...
5065 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5067 if ( !g_list_length(dems) ) {
5068 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5076 * apply_dem_data_common:
5078 * A common function for applying the DEM values and reporting the results.
5080 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5082 if ( !trw_layer_dem_test ( vtl, vlp ) )
5085 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5086 // Inform user how much was changed
5088 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5089 g_snprintf(str, 64, tmp_str, changed);
5090 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5093 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
5095 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5097 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5098 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5100 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5103 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
5106 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
5108 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5110 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5111 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5113 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5116 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
5122 * A common function for applying the elevation smoothing and reporting the results.
5124 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5126 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5127 // Inform user how much was changed
5129 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5130 g_snprintf(str, 64, tmp_str, changed);
5131 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5137 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5139 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5141 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5142 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5144 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5149 smooth_it ( vtl, track, FALSE );
5152 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5154 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5156 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5157 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5159 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5164 smooth_it ( vtl, track, TRUE );
5168 * Commonal helper function
5170 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5173 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5174 g_snprintf(str, 64, tmp_str, changed);
5175 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5178 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5180 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5181 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5183 if ( !trw_layer_dem_test ( vtl, vlp ) )
5187 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5189 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5191 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5195 GHashTableIter iter;
5196 gpointer key, value;
5198 g_hash_table_iter_init ( &iter, vtl->waypoints );
5199 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5200 VikWaypoint *wp = VIK_WAYPOINT(value);
5201 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5204 wp_changed_message ( vtl, changed );
5207 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5209 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5210 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5212 if ( !trw_layer_dem_test ( vtl, vlp ) )
5216 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5218 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5220 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5224 GHashTableIter iter;
5225 gpointer key, value;
5227 g_hash_table_iter_init ( &iter, vtl->waypoints );
5228 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5229 VikWaypoint *wp = VIK_WAYPOINT(value);
5230 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5233 wp_changed_message ( vtl, changed );
5236 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5238 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5240 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5241 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5243 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5248 GList *trps = track->trackpoints;
5251 trps = g_list_last(trps);
5252 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5255 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5257 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5259 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5260 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5262 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5267 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5270 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5273 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5275 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5277 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5278 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5280 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5285 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5288 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5291 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5293 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5295 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5296 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5298 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5303 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5306 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5310 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5312 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5314 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5316 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5317 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5319 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5321 if ( trk && trk->trackpoints )
5323 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5324 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5325 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5326 if ( pass_along[1] )
5327 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5329 vik_layer_emit_update ( VIK_LAYER(vtl) );
5334 * Refine the selected track/route with a routing engine.
5335 * The routing engine is selected by the user, when requestiong the job.
5337 static void trw_layer_route_refine ( gpointer pass_along[6] )
5339 static gint last_engine = 0;
5340 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5343 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5344 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5346 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5348 if ( trk && trk->trackpoints )
5350 /* Check size of the route */
5351 int nb = vik_track_get_tp_count(trk);
5353 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5354 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5355 GTK_MESSAGE_WARNING,
5356 GTK_BUTTONS_OK_CANCEL,
5357 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5359 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5360 gtk_widget_destroy ( dialog );
5361 if (response != GTK_RESPONSE_OK )
5364 /* Select engine from dialog */
5365 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5366 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5367 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5369 GTK_RESPONSE_REJECT,
5371 GTK_RESPONSE_ACCEPT,
5373 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5374 gtk_widget_show_all(label);
5376 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5378 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5379 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5380 gtk_widget_show_all(combo);
5382 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5384 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5386 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5388 /* Dialog validated: retrieve selected engine and do the job */
5389 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5390 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5393 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5395 /* Force saving track */
5396 /* FIXME: remove or rename this hack */
5397 vtl->route_finder_check_added_track = TRUE;
5400 vik_routing_engine_refine (routing, vtl, trk);
5402 /* FIXME: remove or rename this hack */
5403 if ( vtl->route_finder_added_track )
5404 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5406 vtl->route_finder_added_track = NULL;
5407 vtl->route_finder_check_added_track = FALSE;
5409 vik_layer_emit_update ( VIK_LAYER(vtl) );
5411 /* Restore cursor */
5412 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5414 gtk_widget_destroy ( dialog );
5418 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5420 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5421 trw_layer_tpwin_init ( vtl );
5424 /*************************************
5425 * merge/split by time routines
5426 *************************************/
5428 /* called for each key in track hash table.
5429 * If the current track has the same time stamp type, add it to the result,
5430 * except the one pointed by "exclude".
5431 * set exclude to NULL if there is no exclude to check.
5432 * Note that the result is in reverse (for performance reasons).
5437 gboolean with_timestamps;
5439 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5441 twt_udata *user_data = udata;
5442 VikTrackpoint *p1, *p2;
5444 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5448 if (VIK_TRACK(value)->trackpoints) {
5449 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5450 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5452 if ( user_data->with_timestamps ) {
5453 if (!p1->has_timestamp || !p2->has_timestamp) {
5458 // Don't add tracks with timestamps when getting non timestamp tracks
5459 if (p1->has_timestamp || p2->has_timestamp) {
5465 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5468 /* called for each key in track hash table. if original track user_data[1] is close enough
5469 * to the passed one, add it to list in user_data[0]
5471 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5474 VikTrackpoint *p1, *p2;
5475 VikTrack *trk = VIK_TRACK(value);
5477 GList **nearby_tracks = ((gpointer *)user_data)[0];
5478 GList *tpoints = ((gpointer *)user_data)[1];
5481 * detect reasons for not merging, and return
5482 * if no reason is found not to merge, then do it.
5485 // Exclude the original track from the compiled list
5486 if (trk->trackpoints == tpoints) {
5490 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5491 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5493 if (trk->trackpoints) {
5494 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5495 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5497 if (!p1->has_timestamp || !p2->has_timestamp) {
5498 //g_print("no timestamp\n");
5502 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5503 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5504 if (! (abs(t1 - p2->timestamp) < threshold ||
5506 abs(p1->timestamp - t2) < threshold)
5513 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5516 /* comparison function used to sort tracks; a and b are hash table keys */
5517 /* Not actively used - can be restored if needed
5518 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5520 GHashTable *tracks = user_data;
5523 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5524 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5526 if (t1 < t2) return -1;
5527 if (t1 > t2) return 1;
5532 /* comparison function used to sort trackpoints */
5533 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5535 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5537 if (t1 < t2) return -1;
5538 if (t1 > t2) return 1;
5543 * comparison function which can be used to sort tracks or waypoints by name
5545 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5547 const gchar* namea = (const gchar*) a;
5548 const gchar* nameb = (const gchar*) b;
5549 if ( namea == NULL || nameb == NULL)
5552 // Same sort method as used in the vik_treeview_*_alphabetize functions
5553 return strcmp ( namea, nameb );
5557 * Attempt to merge selected track with other tracks specified by the user
5558 * Tracks to merge with must be of the same 'type' as the selected track -
5559 * either all with timestamps, or all without timestamps
5561 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5563 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5564 GList *other_tracks = NULL;
5565 GHashTable *ght_tracks;
5566 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5567 ght_tracks = vtl->routes;
5569 ght_tracks = vtl->tracks;
5571 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5576 if ( !track->trackpoints )
5580 udata.result = &other_tracks;
5581 udata.exclude = track->trackpoints;
5582 // Allow merging with 'similar' time type time tracks
5583 // i.e. either those times, or those without
5584 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5586 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5587 other_tracks = g_list_reverse(other_tracks);
5589 if ( !other_tracks ) {
5590 if ( udata.with_timestamps )
5591 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5593 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5597 // Sort alphabetically for user presentation
5598 // Convert into list of names for usage with dialog function
5599 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5600 GList *other_tracks_names = NULL;
5601 GList *iter = g_list_first ( other_tracks );
5603 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5604 iter = g_list_next ( iter );
5607 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5609 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5613 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5614 g_list_free(other_tracks);
5615 g_list_free(other_tracks_names);
5620 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5621 VikTrack *merge_track;
5622 if ( track->is_route )
5623 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5625 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5628 vik_track_steal_and_append_trackpoints ( track, merge_track );
5629 if ( track->is_route )
5630 vik_trw_layer_delete_route (vtl, merge_track);
5632 vik_trw_layer_delete_track (vtl, merge_track);
5633 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5636 for (l = merge_list; l != NULL; l = g_list_next(l))
5638 g_list_free(merge_list);
5640 vik_layer_emit_update( VIK_LAYER(vtl) );
5644 // c.f. trw_layer_sorted_track_id_by_name_list
5645 // but don't add the specified track to the list (normally current track)
5646 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5648 twt_udata *user_data = udata;
5651 if (trk->trackpoints == user_data->exclude) {
5655 // Sort named list alphabetically
5656 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5660 * Join - this allows combining 'tracks' and 'track routes'
5661 * i.e. doesn't care about whether tracks have consistent timestamps
5662 * ATM can only append one track at a time to the currently selected track
5664 static void trw_layer_append_track ( gpointer pass_along[6] )
5667 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5669 GHashTable *ght_tracks;
5670 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5671 ght_tracks = vtl->routes;
5673 ght_tracks = vtl->tracks;
5675 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5680 GList *other_tracks_names = NULL;
5682 // Sort alphabetically for user presentation
5683 // Convert into list of names for usage with dialog function
5684 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5686 udata.result = &other_tracks_names;
5687 udata.exclude = trk->trackpoints;
5689 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5691 // Note the limit to selecting one track only
5692 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5693 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5694 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5697 trk->is_route ? _("Append Route"): _("Append Track"),
5698 trk->is_route ? _("Select the route to append after the current route") :
5699 _("Select the track to append after the current track") );
5701 g_list_free(other_tracks_names);
5703 // It's a list, but shouldn't contain more than one other track!
5704 if ( append_list ) {
5706 for (l = append_list; l != NULL; l = g_list_next(l)) {
5707 // TODO: at present this uses the first track found by name,
5708 // which with potential multiple same named tracks may not be the one selected...
5709 VikTrack *append_track;
5710 if ( trk->is_route )
5711 append_track = vik_trw_layer_get_route ( vtl, l->data );
5713 append_track = vik_trw_layer_get_track ( vtl, l->data );
5715 if ( append_track ) {
5716 vik_track_steal_and_append_trackpoints ( trk, append_track );
5717 if ( trk->is_route )
5718 vik_trw_layer_delete_route (vtl, append_track);
5720 vik_trw_layer_delete_track (vtl, append_track);
5723 for (l = append_list; l != NULL; l = g_list_next(l))
5725 g_list_free(append_list);
5727 vik_layer_emit_update( VIK_LAYER(vtl) );
5732 * Very similar to trw_layer_append_track for joining
5733 * but this allows selection from the 'other' list
5734 * If a track is selected, then is shows routes and joins the selected one
5735 * If a route is selected, then is shows tracks and joins the selected one
5737 static void trw_layer_append_other ( gpointer pass_along[6] )
5740 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5742 GHashTable *ght_mykind, *ght_others;
5743 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5744 ght_mykind = vtl->routes;
5745 ght_others = vtl->tracks;
5748 ght_mykind = vtl->tracks;
5749 ght_others = vtl->routes;
5752 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5757 GList *other_tracks_names = NULL;
5759 // Sort alphabetically for user presentation
5760 // Convert into list of names for usage with dialog function
5761 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5763 udata.result = &other_tracks_names;
5764 udata.exclude = trk->trackpoints;
5766 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5768 // Note the limit to selecting one track only
5769 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5770 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5771 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5774 trk->is_route ? _("Append Track"): _("Append Route"),
5775 trk->is_route ? _("Select the track to append after the current route") :
5776 _("Select the route to append after the current track") );
5778 g_list_free(other_tracks_names);
5780 // It's a list, but shouldn't contain more than one other track!
5781 if ( append_list ) {
5783 for (l = append_list; l != NULL; l = g_list_next(l)) {
5784 // TODO: at present this uses the first track found by name,
5785 // which with potential multiple same named tracks may not be the one selected...
5787 // Get FROM THE OTHER TYPE list
5788 VikTrack *append_track;
5789 if ( trk->is_route )
5790 append_track = vik_trw_layer_get_track ( vtl, l->data );
5792 append_track = vik_trw_layer_get_route ( vtl, l->data );
5794 if ( append_track ) {
5796 if ( !append_track->is_route &&
5797 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5798 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5800 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5801 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5802 vik_track_merge_segments ( append_track );
5803 vik_track_to_routepoints ( append_track );
5810 vik_track_steal_and_append_trackpoints ( trk, append_track );
5812 // Delete copied which is FROM THE OTHER TYPE list
5813 if ( trk->is_route )
5814 vik_trw_layer_delete_track (vtl, append_track);
5816 vik_trw_layer_delete_route (vtl, append_track);
5819 for (l = append_list; l != NULL; l = g_list_next(l))
5821 g_list_free(append_list);
5822 vik_layer_emit_update( VIK_LAYER(vtl) );
5826 /* merge by segments */
5827 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5829 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5830 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5831 guint segments = vik_track_merge_segments ( trk );
5832 // NB currently no need to redraw as segments not actually shown on the display
5833 // However inform the user of what happened:
5835 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5836 g_snprintf(str, 64, tmp_str, segments);
5837 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5840 /* merge by time routine */
5841 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5843 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5847 GList *tracks_with_timestamp = NULL;
5848 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5849 if (orig_trk->trackpoints &&
5850 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5851 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5856 udata.result = &tracks_with_timestamp;
5857 udata.exclude = orig_trk->trackpoints;
5858 udata.with_timestamps = TRUE;
5859 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5860 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5862 if (!tracks_with_timestamp) {
5863 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5866 g_list_free(tracks_with_timestamp);
5868 static guint threshold_in_minutes = 1;
5869 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5870 _("Merge Threshold..."),
5871 _("Merge when time between tracks less than:"),
5872 &threshold_in_minutes)) {
5876 // keep attempting to merge all tracks until no merges within the time specified is possible
5877 gboolean attempt_merge = TRUE;
5878 GList *nearby_tracks = NULL;
5880 static gpointer params[3];
5882 while ( attempt_merge ) {
5884 // Don't try again unless tracks have changed
5885 attempt_merge = FALSE;
5887 trps = orig_trk->trackpoints;
5891 if (nearby_tracks) {
5892 g_list_free(nearby_tracks);
5893 nearby_tracks = NULL;
5896 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5897 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5899 /* g_print("Original track times: %d and %d\n", t1, t2); */
5900 params[0] = &nearby_tracks;
5901 params[1] = (gpointer)trps;
5902 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5904 /* get a list of adjacent-in-time tracks */
5905 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5908 GList *l = nearby_tracks;
5911 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5912 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5914 t1 = get_first_trackpoint(l)->timestamp;
5915 t2 = get_last_trackpoint(l)->timestamp;
5916 #undef get_first_trackpoint
5917 #undef get_last_trackpoint
5918 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5921 /* remove trackpoints from merged track, delete track */
5922 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5923 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5925 // Tracks have changed, therefore retry again against all the remaining tracks
5926 attempt_merge = TRUE;
5931 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5934 g_list_free(nearby_tracks);
5936 vik_layer_emit_update( VIK_LAYER(vtl) );
5940 * Split a track at the currently selected trackpoint
5942 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5944 if ( !vtl->current_tpl )
5947 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5948 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5950 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5951 GList *newglist = g_list_alloc ();
5952 newglist->prev = NULL;
5953 newglist->next = vtl->current_tpl->next;
5954 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5955 tr->trackpoints = newglist;
5957 vtl->current_tpl->next->prev = newglist; /* end old track here */
5958 vtl->current_tpl->next = NULL;
5960 // Bounds of the selected track changed due to the split
5961 vik_track_calculate_bounds ( vtl->current_tp_track );
5963 vtl->current_tpl = newglist; /* change tp to first of new track. */
5964 vtl->current_tp_track = tr;
5967 vik_trw_layer_add_route ( vtl, name, tr );
5969 vik_trw_layer_add_track ( vtl, name, tr );
5971 // Bounds of the new track created by the split
5972 vik_track_calculate_bounds ( tr );
5978 // Also need id of newly created track
5981 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5983 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5985 if ( trkf && udata.uuid )
5986 vtl->current_tp_id = udata.uuid;
5988 vtl->current_tp_id = NULL;
5990 vik_layer_emit_update(VIK_LAYER(vtl));
5996 /* split by time routine */
5997 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5999 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6000 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6001 GList *trps = track->trackpoints;
6003 GList *newlists = NULL;
6004 GList *newtps = NULL;
6005 static guint thr = 1;
6012 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6013 _("Split Threshold..."),
6014 _("Split when time between trackpoints exceeds:"),
6019 /* iterate through trackpoints, and copy them into new lists without touching original list */
6020 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6024 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6026 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6029 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6030 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6031 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6033 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
6038 if (ts - prev_ts > thr*60) {
6039 /* flush accumulated trackpoints into new list */
6040 newlists = g_list_append(newlists, g_list_reverse(newtps));
6044 /* accumulate trackpoint copies in newtps, in reverse order */
6045 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6047 iter = g_list_next(iter);
6050 newlists = g_list_append(newlists, g_list_reverse(newtps));
6053 /* put lists of trackpoints into tracks */
6055 // Only bother updating if the split results in new tracks
6056 if (g_list_length (newlists) > 1) {
6061 tr = vik_track_copy ( track, FALSE );
6062 tr->trackpoints = (GList *)(iter->data);
6064 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6065 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6066 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
6067 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
6068 g_free ( new_tr_name );
6069 vik_track_calculate_bounds ( tr );
6070 iter = g_list_next(iter);
6072 // Remove original track and then update the display
6073 vik_trw_layer_delete_track (vtl, track);
6074 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6076 g_list_free(newlists);
6080 * Split a track by the number of points as specified by the user
6082 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
6084 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6086 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6087 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6089 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6094 // Check valid track
6095 GList *trps = track->trackpoints;
6099 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6100 _("Split Every Nth Point"),
6101 _("Split on every Nth point:"),
6102 250, // Default value as per typical limited track capacity of various GPS devices
6106 // Was a valid number returned?
6112 GList *newlists = NULL;
6113 GList *newtps = NULL;
6118 /* accumulate trackpoint copies in newtps, in reverse order */
6119 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6121 if (count >= points) {
6122 /* flush accumulated trackpoints into new list */
6123 newlists = g_list_append(newlists, g_list_reverse(newtps));
6127 iter = g_list_next(iter);
6130 // If there is a remaining chunk put that into the new split list
6131 // This may well be the whole track if no split points were encountered
6133 newlists = g_list_append(newlists, g_list_reverse(newtps));
6136 /* put lists of trackpoints into tracks */
6138 // Only bother updating if the split results in new tracks
6139 if (g_list_length (newlists) > 1) {
6144 tr = vik_track_copy ( track, FALSE );
6145 tr->trackpoints = (GList *)(iter->data);
6147 if ( track->is_route ) {
6148 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6149 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6152 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6153 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6155 g_free ( new_tr_name );
6156 vik_track_calculate_bounds ( tr );
6158 iter = g_list_next(iter);
6160 // Remove original track and then update the display
6161 if ( track->is_route )
6162 vik_trw_layer_delete_route (vtl, track);
6164 vik_trw_layer_delete_track (vtl, track);
6165 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6167 g_list_free(newlists);
6171 * Split a track at the currently selected trackpoint
6173 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6175 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6176 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6177 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6181 * Split a track by its segments
6182 * Routes do not have segments so don't call this for routes
6184 static void trw_layer_split_segments ( gpointer pass_along[6] )
6186 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6187 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6194 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6197 for ( i = 0; i < ntracks; i++ ) {
6199 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6200 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6201 g_free ( new_tr_name );
6206 // Remove original track
6207 vik_trw_layer_delete_track ( vtl, trk );
6208 vik_layer_emit_update ( VIK_LAYER(vtl) );
6211 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6214 /* end of split/merge routines */
6216 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6220 // Find available adjacent trackpoint
6221 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6222 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6223 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6225 // Delete current trackpoint
6226 vik_trackpoint_free ( vtl->current_tpl->data );
6227 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6229 // Set to current to the available adjacent trackpoint
6230 vtl->current_tpl = new_tpl;
6232 if ( vtl->current_tp_track ) {
6233 vik_track_calculate_bounds ( vtl->current_tp_track );
6237 // Delete current trackpoint
6238 vik_trackpoint_free ( vtl->current_tpl->data );
6239 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6240 trw_layer_cancel_current_tp ( vtl, FALSE );
6245 * Delete the selected point
6247 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6249 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6251 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6252 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6254 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6259 if ( !vtl->current_tpl )
6262 trw_layer_trackpoint_selected_delete ( vtl, trk );
6264 // Track has been updated so update tps:
6265 trw_layer_cancel_tps_of_track ( vtl, trk );
6267 vik_layer_emit_update ( VIK_LAYER(vtl) );
6271 * Delete adjacent track points at the same position
6272 * AKA Delete Dulplicates on the Properties Window
6274 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6276 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6278 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6279 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6281 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6286 gulong removed = vik_track_remove_dup_points ( trk );
6288 // Track has been updated so update tps:
6289 trw_layer_cancel_tps_of_track ( vtl, trk );
6291 // Inform user how much was deleted as it's not obvious from the normal view
6293 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6294 g_snprintf(str, 64, tmp_str, removed);
6295 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6297 vik_layer_emit_update ( VIK_LAYER(vtl) );
6301 * Delete adjacent track points with the same timestamp
6302 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6304 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6306 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6308 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6309 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6311 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6316 gulong removed = vik_track_remove_same_time_points ( trk );
6318 // Track has been updated so update tps:
6319 trw_layer_cancel_tps_of_track ( vtl, trk );
6321 // Inform user how much was deleted as it's not obvious from the normal view
6323 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6324 g_snprintf(str, 64, tmp_str, removed);
6325 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6327 vik_layer_emit_update ( VIK_LAYER(vtl) );
6333 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6335 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6337 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6338 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6340 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6345 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6347 vik_layer_emit_update ( VIK_LAYER(vtl) );
6350 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6352 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6354 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6355 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6357 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6362 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6364 vik_layer_emit_update ( VIK_LAYER(vtl) );
6370 static void trw_layer_reverse ( gpointer pass_along[6] )
6372 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6374 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6375 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6377 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6382 vik_track_reverse ( track );
6384 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6388 * Similar to trw_layer_enum_item, but this uses a sorted method
6391 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6393 GList **list = (GList**)udata;
6394 // *list = g_list_prepend(*all, key); //unsorted method
6395 // Sort named list alphabetically
6396 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6401 * Now Waypoint specific sort
6403 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6405 GList **list = (GList**)udata;
6406 // Sort named list alphabetically
6407 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6411 * Track specific sort
6413 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6415 GList **list = (GList**)udata;
6416 // Sort named list alphabetically
6417 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6422 gboolean has_same_track_name;
6423 const gchar *same_track_name;
6424 } same_track_name_udata;
6426 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6428 const gchar* namea = (const gchar*) aa;
6429 const gchar* nameb = (const gchar*) bb;
6432 gint result = strcmp ( namea, nameb );
6434 if ( result == 0 ) {
6435 // Found two names the same
6436 same_track_name_udata *user_data = udata;
6437 user_data->has_same_track_name = TRUE;
6438 user_data->same_track_name = namea;
6441 // Leave ordering the same
6446 * Find out if any tracks have the same name in this hash table
6448 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6450 // Sort items by name, then compare if any next to each other are the same
6452 GList *track_names = NULL;
6453 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6456 if ( ! track_names )
6459 same_track_name_udata udata;
6460 udata.has_same_track_name = FALSE;
6462 // Use sort routine to traverse list comparing items
6463 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6464 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6465 // Still no tracks...
6469 return udata.has_same_track_name;
6473 * Force unqiue track names for the track table specified
6474 * Note the panel is a required parameter to enable the update of the names displayed
6475 * Specify if on tracks or else on routes
6477 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6479 // . Search list for an instance of repeated name
6480 // . get track of this name
6481 // . create new name
6482 // . rename track & update equiv. treeview iter
6483 // . repeat until all different
6485 same_track_name_udata udata;
6487 GList *track_names = NULL;
6488 udata.has_same_track_name = FALSE;
6489 udata.same_track_name = NULL;
6491 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6494 if ( ! track_names )
6497 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6499 // Still no tracks...
6500 if ( ! dummy_list1 )
6503 while ( udata.has_same_track_name ) {
6505 // Find a track with the same name
6508 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6510 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6514 g_critical("Houston, we've had a problem.");
6515 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6516 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6521 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6522 vik_track_set_name ( trk, newname );
6528 // Need want key of it for treeview update
6529 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6531 if ( trkf && udataU.uuid ) {
6535 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6537 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6540 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6542 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6544 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6548 // Start trying to find same names again...
6550 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6551 udata.has_same_track_name = FALSE;
6552 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6554 // No tracks any more - give up searching
6555 if ( ! dummy_list2 )
6556 udata.has_same_track_name = FALSE;
6560 vik_layers_panel_emit_update ( vlp );
6563 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6565 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6568 switch (GPOINTER_TO_INT (pass_along[2])) {
6569 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6570 iter = &(vtl->tracks_iter);
6571 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6573 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6574 iter = &(vtl->routes_iter);
6575 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6577 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6578 iter = &(vtl->waypoints_iter);
6579 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6583 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6586 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6588 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6591 switch (GPOINTER_TO_INT (pass_along[2])) {
6592 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6593 iter = &(vtl->tracks_iter);
6594 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6596 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6597 iter = &(vtl->routes_iter);
6598 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6600 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6601 iter = &(vtl->waypoints_iter);
6602 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6606 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6612 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6614 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6617 // Ensure list of track names offered is unique
6618 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6619 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6620 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6621 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6627 // Sort list alphabetically for better presentation
6628 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6631 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6635 // Get list of items to delete from the user
6636 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6639 _("Delete Selection"),
6640 _("Select tracks to delete"));
6643 // Delete requested tracks
6644 // since specificly requested, IMHO no need for extra confirmation
6645 if ( delete_list ) {
6647 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6648 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6649 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6651 g_list_free(delete_list);
6652 vik_layer_emit_update( VIK_LAYER(vtl) );
6659 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6661 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6664 // Ensure list of track names offered is unique
6665 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6666 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6667 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6668 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6674 // Sort list alphabetically for better presentation
6675 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6678 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6682 // Get list of items to delete from the user
6683 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6686 _("Delete Selection"),
6687 _("Select routes to delete") );
6690 // Delete requested routes
6691 // since specificly requested, IMHO no need for extra confirmation
6692 if ( delete_list ) {
6694 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6695 // This deletes first route it finds of that name (but uniqueness is enforced above)
6696 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6698 g_list_free(delete_list);
6699 vik_layer_emit_update( VIK_LAYER(vtl) );
6704 gboolean has_same_waypoint_name;
6705 const gchar *same_waypoint_name;
6706 } same_waypoint_name_udata;
6708 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6710 const gchar* namea = (const gchar*) aa;
6711 const gchar* nameb = (const gchar*) bb;
6714 gint result = strcmp ( namea, nameb );
6716 if ( result == 0 ) {
6717 // Found two names the same
6718 same_waypoint_name_udata *user_data = udata;
6719 user_data->has_same_waypoint_name = TRUE;
6720 user_data->same_waypoint_name = namea;
6723 // Leave ordering the same
6728 * Find out if any waypoints have the same name in this layer
6730 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6732 // Sort items by name, then compare if any next to each other are the same
6734 GList *waypoint_names = NULL;
6735 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6738 if ( ! waypoint_names )
6741 same_waypoint_name_udata udata;
6742 udata.has_same_waypoint_name = FALSE;
6744 // Use sort routine to traverse list comparing items
6745 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6746 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6747 // Still no waypoints...
6751 return udata.has_same_waypoint_name;
6755 * Force unqiue waypoint names for this layer
6756 * Note the panel is a required parameter to enable the update of the names displayed
6758 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6760 // . Search list for an instance of repeated name
6761 // . get waypoint of this name
6762 // . create new name
6763 // . rename waypoint & update equiv. treeview iter
6764 // . repeat until all different
6766 same_waypoint_name_udata udata;
6768 GList *waypoint_names = NULL;
6769 udata.has_same_waypoint_name = FALSE;
6770 udata.same_waypoint_name = NULL;
6772 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6775 if ( ! waypoint_names )
6778 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6780 // Still no waypoints...
6781 if ( ! dummy_list1 )
6784 while ( udata.has_same_waypoint_name ) {
6786 // Find a waypoint with the same name
6787 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6791 g_critical("Houston, we've had a problem.");
6792 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6793 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6798 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6800 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6802 // Start trying to find same names again...
6803 waypoint_names = NULL;
6804 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6805 udata.has_same_waypoint_name = FALSE;
6806 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6808 // No waypoints any more - give up searching
6809 if ( ! dummy_list2 )
6810 udata.has_same_waypoint_name = FALSE;
6814 vik_layers_panel_emit_update ( vlp );
6820 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6822 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6825 // Ensure list of waypoint names offered is unique
6826 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6827 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6828 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6829 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6835 // Sort list alphabetically for better presentation
6836 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6838 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6842 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6844 // Get list of items to delete from the user
6845 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6848 _("Delete Selection"),
6849 _("Select waypoints to delete"));
6852 // Delete requested waypoints
6853 // since specificly requested, IMHO no need for extra confirmation
6854 if ( delete_list ) {
6856 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6857 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6858 trw_layer_delete_waypoint_by_name (vtl, l->data);
6860 g_list_free(delete_list);
6862 trw_layer_calculate_bounds_waypoints ( vtl );
6863 vik_layer_emit_update( VIK_LAYER(vtl) );
6871 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6873 vik_treeview_item_toggle_visible ( vt, it );
6879 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6881 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6887 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6889 wp->visible = GPOINTER_TO_INT (on_off);
6895 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6897 wp->visible = !wp->visible;
6903 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6905 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6906 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6907 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6908 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6910 vik_layer_emit_update ( VIK_LAYER(vtl) );
6916 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6918 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6919 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6920 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6921 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6923 vik_layer_emit_update ( VIK_LAYER(vtl) );
6929 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6931 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6932 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6933 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6935 vik_layer_emit_update ( VIK_LAYER(vtl) );
6941 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6943 trk->visible = GPOINTER_TO_INT (on_off);
6949 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6951 trk->visible = !trk->visible;
6957 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6959 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6960 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6961 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6962 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6964 vik_layer_emit_update ( VIK_LAYER(vtl) );
6970 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6972 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6973 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6974 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6975 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6977 vik_layer_emit_update ( VIK_LAYER(vtl) );
6983 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6985 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6986 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6987 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6989 vik_layer_emit_update ( VIK_LAYER(vtl) );
6995 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6997 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6998 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6999 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7000 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7002 vik_layer_emit_update ( VIK_LAYER(vtl) );
7008 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
7010 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7011 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7012 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7013 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7015 vik_layer_emit_update ( VIK_LAYER(vtl) );
7021 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
7023 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7024 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7025 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7027 vik_layer_emit_update ( VIK_LAYER(vtl) );
7031 * vik_trw_layer_build_waypoint_list_t:
7033 * Helper function to construct a list of #vik_trw_waypoint_list_t
7035 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7037 GList *waypoints_and_layers = NULL;
7038 // build waypoints_and_layers list
7039 while ( waypoints ) {
7040 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7041 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7043 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7044 waypoints = g_list_next ( waypoints );
7046 return waypoints_and_layers;
7050 * trw_layer_create_waypoint_list:
7052 * Create the latest list of waypoints with the associated layer(s)
7053 * Although this will always be from a single layer here
7055 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7057 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7058 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7060 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7064 * trw_layer_analyse_close:
7066 * Stuff to do on dialog closure
7068 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7070 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7071 gtk_widget_destroy ( dialog );
7072 vtl->tracks_analysis_dialog = NULL;
7076 * vik_trw_layer_build_track_list_t:
7078 * Helper function to construct a list of #vik_trw_track_list_t
7080 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7082 GList *tracks_and_layers = NULL;
7083 // build tracks_and_layers list
7085 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7086 vtdl->trk = VIK_TRACK(tracks->data);
7088 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7089 tracks = g_list_next ( tracks );
7091 return tracks_and_layers;
7095 * trw_layer_create_track_list:
7097 * Create the latest list of tracks with the associated layer(s)
7098 * Although this will always be from a single layer here
7100 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7102 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7103 GList *tracks = NULL;
7104 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7105 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7107 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7109 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7112 static void trw_layer_tracks_stats ( gpointer lav[2] )
7114 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7115 // There can only be one!
7116 if ( vtl->tracks_analysis_dialog )
7119 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7120 VIK_LAYER(vtl)->name,
7122 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7123 trw_layer_create_track_list,
7124 trw_layer_analyse_close );
7130 static void trw_layer_routes_stats ( gpointer lav[2] )
7132 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7133 // There can only be one!
7134 if ( vtl->tracks_analysis_dialog )
7137 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7138 VIK_LAYER(vtl)->name,
7140 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7141 trw_layer_create_track_list,
7142 trw_layer_analyse_close );
7145 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
7147 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7149 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
7152 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
7154 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7157 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7158 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7162 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
7164 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7167 if ( !strncmp(wp->comment, "http", 4) ) {
7168 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7169 } else if ( !strncmp(wp->description, "http", 4) ) {
7170 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7174 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7176 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7178 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7180 // No actual change to the name supplied
7182 if (strcmp(newname, wp->name) == 0 )
7185 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7188 // An existing waypoint has been found with the requested name
7189 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7190 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7195 // Update WP name and refresh the treeview
7196 vik_waypoint_set_name (wp, newname);
7198 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7199 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7201 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7206 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7208 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7210 // No actual change to the name supplied
7212 if (strcmp(newname, trk->name) == 0)
7215 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7218 // An existing track has been found with the requested name
7219 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7220 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7224 // Update track name and refresh GUI parts
7225 vik_track_set_name (trk, newname);
7227 // Update any subwindows that could be displaying this track which has changed name
7228 // Only one Track Edit Window
7229 if ( l->current_tp_track == trk && l->tpwin ) {
7230 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7232 // Property Dialog of the track
7233 vik_trw_layer_propwin_update ( trk );
7235 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7236 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7238 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7245 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7247 // No actual change to the name supplied
7249 if (strcmp(newname, trk->name) == 0)
7252 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7255 // An existing track has been found with the requested name
7256 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7257 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7261 // Update track name and refresh GUI parts
7262 vik_track_set_name (trk, newname);
7264 // Update any subwindows that could be displaying this track which has changed name
7265 // Only one Track Edit Window
7266 if ( l->current_tp_track == trk && l->tpwin ) {
7267 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7269 // Property Dialog of the track
7270 vik_trw_layer_propwin_update ( trk );
7272 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7273 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7275 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7282 static gboolean is_valid_geocache_name ( gchar *str )
7284 gint len = strlen ( str );
7285 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]));
7288 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7290 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7291 a_acquire_set_filter_track ( trk );
7294 #ifdef VIK_CONFIG_GOOGLE
7295 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7297 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7298 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7301 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7303 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7305 gchar *escaped = uri_escape ( tr->comment );
7306 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7307 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7314 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7315 /* viewpoint is now available instead */
7316 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7318 static gpointer pass_along[8];
7320 gboolean rv = FALSE;
7323 pass_along[1] = vlp;
7324 pass_along[2] = GINT_TO_POINTER (subtype);
7325 pass_along[3] = sublayer;
7326 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7327 pass_along[5] = vvp;
7328 pass_along[6] = iter;
7329 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7331 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7335 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7338 gtk_widget_show ( item );
7340 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7341 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7342 if (tr && tr->property_dialog)
7343 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7345 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7346 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7347 if (tr && tr->property_dialog)
7348 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7351 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7353 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7354 gtk_widget_show ( item );
7356 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7358 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7359 gtk_widget_show ( item );
7361 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7363 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7364 gtk_widget_show ( item );
7366 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7368 // Always create separator as now there is always at least the transform menu option
7369 item = gtk_menu_item_new ();
7370 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7371 gtk_widget_show ( item );
7373 /* could be a right-click using the tool */
7374 if ( vlp != NULL ) {
7375 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7376 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7378 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7379 gtk_widget_show ( item );
7382 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7384 if ( wp && wp->name ) {
7385 if ( is_valid_geocache_name ( wp->name ) ) {
7386 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7389 gtk_widget_show ( item );
7391 #ifdef VIK_CONFIG_GEOTAG
7392 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7393 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7394 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7395 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7396 gtk_widget_show ( item );
7400 if ( wp && wp->image )
7402 // Set up image paramater
7403 pass_along[5] = wp->image;
7405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7406 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
7407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7408 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7409 gtk_widget_show ( item );
7411 #ifdef VIK_CONFIG_GEOTAG
7412 GtkWidget *geotag_submenu = gtk_menu_new ();
7413 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7414 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7415 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7416 gtk_widget_show ( item );
7417 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7419 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7420 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7421 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7422 gtk_widget_show ( item );
7424 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7426 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7427 gtk_widget_show ( item );
7433 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7434 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7435 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7436 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7438 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7439 gtk_widget_show ( item );
7445 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7446 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7448 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7449 gtk_widget_show ( item );
7450 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7451 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7452 gtk_widget_set_sensitive ( item, TRUE );
7454 gtk_widget_set_sensitive ( item, FALSE );
7457 item = gtk_menu_item_new ();
7458 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7459 gtk_widget_show ( item );
7462 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7465 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7466 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7468 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7469 gtk_widget_show ( item );
7472 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7474 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7475 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7476 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7477 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7478 gtk_widget_show ( item );
7480 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7482 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7483 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7484 gtk_widget_show ( item );
7486 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7490 gtk_widget_show ( item );
7492 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7495 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7496 gtk_widget_show ( item );
7498 GtkWidget *vis_submenu = gtk_menu_new ();
7499 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7500 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7501 gtk_widget_show ( item );
7502 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7507 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7508 gtk_widget_show ( item );
7510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7513 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7514 gtk_widget_show ( item );
7516 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7517 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7519 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7520 gtk_widget_show ( item );
7522 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7528 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7532 if ( l->current_track && !l->current_track->is_route ) {
7533 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7535 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7536 gtk_widget_show ( item );
7538 item = gtk_menu_item_new ();
7539 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7540 gtk_widget_show ( item );
7543 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7544 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7545 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7546 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7547 gtk_widget_show ( item );
7549 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7550 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7551 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7552 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7553 gtk_widget_show ( item );
7554 // Make it available only when a new track *not* already in progress
7555 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7557 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7561 gtk_widget_show ( item );
7563 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7566 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7567 gtk_widget_show ( item );
7569 GtkWidget *vis_submenu = gtk_menu_new ();
7570 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7571 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7572 gtk_widget_show ( item );
7573 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7575 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7576 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7578 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7579 gtk_widget_show ( item );
7581 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7582 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7585 gtk_widget_show ( item );
7587 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7588 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7592 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7593 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7595 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7596 gtk_widget_show ( item );
7598 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7600 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7601 gtk_widget_show ( item );
7604 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7608 if ( l->current_track && l->current_track->is_route ) {
7609 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7610 // Reuse finish track method
7611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7612 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7613 gtk_widget_show ( item );
7615 item = gtk_menu_item_new ();
7616 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7617 gtk_widget_show ( item );
7620 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7623 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7624 gtk_widget_show ( item );
7626 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7627 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7629 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7630 gtk_widget_show ( item );
7631 // Make it available only when a new track *not* already in progress
7632 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7634 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7635 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7636 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7637 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7638 gtk_widget_show ( item );
7640 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7642 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7643 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7644 gtk_widget_show ( item );
7646 GtkWidget *vis_submenu = gtk_menu_new ();
7647 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7648 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7649 gtk_widget_show ( item );
7650 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7652 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7653 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), 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 ( _("_Hide All Routes") );
7659 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7662 gtk_widget_show ( item );
7664 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7665 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7667 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7669 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7670 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7674 gtk_widget_show ( item );
7676 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7677 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7678 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7679 gtk_widget_show ( item );
7683 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7684 GtkWidget *submenu_sort = gtk_menu_new ();
7685 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7686 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7687 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7688 gtk_widget_show ( item );
7689 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7691 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7692 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7693 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7694 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7695 gtk_widget_show ( item );
7697 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7698 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7700 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7701 gtk_widget_show ( item );
7704 GtkWidget *upload_submenu = gtk_menu_new ();
7706 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7708 item = gtk_menu_item_new ();
7709 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7710 gtk_widget_show ( item );
7712 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7713 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7714 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7715 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7716 if ( l->current_track ) {
7717 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7718 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7719 gtk_widget_show ( item );
7722 item = gtk_menu_item_new ();
7723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7724 gtk_widget_show ( item );
7727 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7728 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7730 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7731 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7733 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7734 gtk_widget_show ( item );
7736 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7739 gtk_widget_show ( item );
7741 GtkWidget *goto_submenu;
7742 goto_submenu = gtk_menu_new ();
7743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7746 gtk_widget_show ( item );
7747 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7749 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7752 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7753 gtk_widget_show ( item );
7755 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7756 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7759 gtk_widget_show ( item );
7761 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7764 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7765 gtk_widget_show ( item );
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7770 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7771 gtk_widget_show ( item );
7773 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7776 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7777 gtk_widget_show ( item );
7779 // Routes don't have speeds
7780 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7781 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7782 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7784 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7785 gtk_widget_show ( item );
7788 GtkWidget *combine_submenu;
7789 combine_submenu = gtk_menu_new ();
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7792 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7793 gtk_widget_show ( item );
7794 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7796 // Routes don't have times or segments...
7797 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7798 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7801 gtk_widget_show ( item );
7803 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7805 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7806 gtk_widget_show ( item );
7809 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7812 gtk_widget_show ( item );
7814 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7815 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7817 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7820 gtk_widget_show ( item );
7822 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7823 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7825 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7827 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7828 gtk_widget_show ( item );
7830 GtkWidget *split_submenu;
7831 split_submenu = gtk_menu_new ();
7832 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7833 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7835 gtk_widget_show ( item );
7836 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7838 // Routes don't have times or segments...
7839 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7840 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7842 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7843 gtk_widget_show ( item );
7845 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7846 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7848 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7849 gtk_widget_show ( item );
7852 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7854 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7855 gtk_widget_show ( item );
7857 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7859 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7860 gtk_widget_show ( item );
7861 // Make it available only when a trackpoint is selected.
7862 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7864 GtkWidget *insert_submenu = gtk_menu_new ();
7865 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7866 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7867 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7868 gtk_widget_show ( item );
7869 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7871 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7873 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7874 gtk_widget_show ( item );
7875 // Make it available only when a point is selected
7876 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7878 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7881 gtk_widget_show ( item );
7882 // Make it available only when a point is selected
7883 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7885 GtkWidget *delete_submenu;
7886 delete_submenu = gtk_menu_new ();
7887 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7889 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7890 gtk_widget_show ( item );
7891 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7893 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7896 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7897 gtk_widget_show ( item );
7898 // Make it available only when a point is selected
7899 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7901 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7904 gtk_widget_show ( item );
7906 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7908 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7909 gtk_widget_show ( item );
7911 GtkWidget *transform_submenu;
7912 transform_submenu = gtk_menu_new ();
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7915 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7916 gtk_widget_show ( item );
7917 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7919 GtkWidget *dem_submenu;
7920 dem_submenu = gtk_menu_new ();
7921 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7922 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
7923 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7924 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7929 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7930 gtk_widget_show ( item );
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7934 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7935 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7936 gtk_widget_show ( item );
7938 GtkWidget *smooth_submenu;
7939 smooth_submenu = gtk_menu_new ();
7940 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7941 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7942 gtk_widget_show ( item );
7943 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7945 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7948 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7949 gtk_widget_show ( item );
7951 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7954 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7955 gtk_widget_show ( item );
7957 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7960 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7961 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7963 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7964 gtk_widget_show ( item );
7966 // Routes don't have timestamps - so this is only available for tracks
7967 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7968 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7970 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7971 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7972 gtk_widget_show ( item );
7975 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7978 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7979 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7982 gtk_widget_show ( item );
7984 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7985 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7988 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7989 gtk_widget_show ( item );
7992 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7994 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7995 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7998 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
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8001 gtk_widget_show ( item );
8004 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8005 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8010 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8011 gtk_widget_show ( item );
8013 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8016 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8019 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8020 gtk_widget_show ( item );
8022 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8023 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8024 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
8025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8027 gtk_widget_show ( item );
8030 // ATM can't upload a single waypoint but can do waypoints to a GPS
8031 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8032 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8034 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8035 gtk_widget_show ( item );
8036 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8038 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8041 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8042 gtk_widget_show ( item );
8046 #ifdef VIK_CONFIG_GOOGLE
8047 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8049 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8050 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8051 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8053 gtk_widget_show ( item );
8057 // Some things aren't usable with routes
8058 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8059 #ifdef VIK_CONFIG_OPENSTREETMAP
8060 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8061 // Convert internal pointer into actual track for usage outside this file
8062 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
8063 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8064 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
8065 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8066 gtk_widget_show ( item );
8069 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8073 gtk_widget_show ( item );
8075 /* ATM This function is only available via the layers panel, due to needing a vlp */
8077 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8078 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8079 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8081 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8082 gtk_widget_show ( item );
8086 #ifdef VIK_CONFIG_GEOTAG
8087 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8090 gtk_widget_show ( item );
8094 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8095 // Only show on viewport popmenu when a trackpoint is selected
8096 if ( ! vlp && l->current_tpl ) {
8098 item = gtk_menu_item_new ();
8099 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8100 gtk_widget_show ( item );
8102 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8103 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8105 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8106 gtk_widget_show ( item );
8110 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8111 GtkWidget *transform_submenu;
8112 transform_submenu = gtk_menu_new ();
8113 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8115 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8116 gtk_widget_show ( item );
8117 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8119 GtkWidget *dem_submenu;
8120 dem_submenu = gtk_menu_new ();
8121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8122 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
8123 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8124 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8126 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8128 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8129 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8130 gtk_widget_show ( item );
8132 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8134 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8135 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8136 gtk_widget_show ( item );
8139 gtk_widget_show_all ( GTK_WIDGET(menu) );
8144 // TODO: Probably better to rework this track manipulation in viktrack.c
8145 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8148 if (!vtl->current_tpl)
8151 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8152 VikTrackpoint *tp_other = NULL;
8155 if (!vtl->current_tpl->prev)
8157 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8159 if (!vtl->current_tpl->next)
8161 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8164 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8167 VikTrackpoint *tp_new = vik_trackpoint_new();
8168 struct LatLon ll_current, ll_other;
8169 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8170 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8172 /* main positional interpolation */
8173 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8174 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8176 /* Now other properties that can be interpolated */
8177 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8179 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8180 /* Note here the division is applied to each part, then added
8181 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8182 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8183 tp_new->has_timestamp = TRUE;
8186 if (tp_current->speed != NAN && tp_other->speed != NAN)
8187 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8189 /* TODO - improve interpolation of course, as it may not be correct.
8190 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8191 [similar applies if value is in radians] */
8192 if (tp_current->course != NAN && tp_other->course != NAN)
8193 tp_new->course = (tp_current->course + tp_other->course)/2;
8195 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8197 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8198 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8200 // Otherwise try routes
8201 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8205 gint index = g_list_index ( trk->trackpoints, tp_current );
8209 // NB no recalculation of bounds since it is inserted between points
8210 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8215 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8221 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8225 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8227 if ( vtl->current_tpl )
8229 vtl->current_tpl = NULL;
8230 vtl->current_tp_track = NULL;
8231 vtl->current_tp_id = NULL;
8232 vik_layer_emit_update(VIK_LAYER(vtl));
8236 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8238 g_assert ( vtl->tpwin != NULL );
8239 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8240 trw_layer_cancel_current_tp ( vtl, TRUE );
8242 if ( vtl->current_tpl == NULL )
8245 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8247 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8248 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8250 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8252 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8254 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8258 trw_layer_trackpoint_selected_delete ( vtl, tr );
8260 if ( vtl->current_tpl )
8261 // Reset dialog with the available adjacent trackpoint
8262 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8264 vik_layer_emit_update(VIK_LAYER(vtl));
8266 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8268 if ( vtl->current_tp_track )
8269 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8270 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8272 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8274 if ( vtl->current_tp_track )
8275 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8276 vik_layer_emit_update(VIK_LAYER(vtl));
8278 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8280 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8281 vik_layer_emit_update(VIK_LAYER(vtl));
8283 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8284 vik_layer_emit_update(VIK_LAYER(vtl));
8288 * trw_layer_dialog_shift:
8289 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8291 * Try to reposition a dialog if it's over the specified coord
8292 * so to not obscure the item of interest
8294 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8296 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8298 // Attempt force dialog to be shown so we can find out where it is more reliably...
8299 while ( gtk_events_pending() )
8300 gtk_main_iteration ();
8302 // get parent window position & size
8303 gint win_pos_x, win_pos_y;
8304 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8306 gint win_size_x, win_size_y;
8307 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8309 // get own dialog size
8310 gint dia_size_x, dia_size_y;
8311 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8313 // get own dialog position
8314 gint dia_pos_x, dia_pos_y;
8315 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8317 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8318 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8320 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8322 gint vp_xx, vp_yy; // In viewport pixels
8323 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8325 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8329 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8331 // Transform Viewport pixels into absolute pixels
8332 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8333 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8335 // Is dialog over the point (to within an ^^ edge value)
8336 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8337 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8341 gint hh = vik_viewport_get_height ( vvp );
8343 // Consider the difference in viewport to the full window
8344 gint offset_y = dest_y;
8345 // Add difference between dialog and window sizes
8346 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8348 if ( vp_yy > hh/2 ) {
8349 // Point in bottom half, move window to top half
8350 gtk_window_move ( dialog, dia_pos_x, offset_y );
8353 // Point in top half, move dialog down
8354 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8358 // Shift left<->right
8359 gint ww = vik_viewport_get_width ( vvp );
8361 // Consider the difference in viewport to the full window
8362 gint offset_x = dest_x;
8363 // Add difference between dialog and window sizes
8364 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8366 if ( vp_xx > ww/2 ) {
8367 // Point on right, move window to left
8368 gtk_window_move ( dialog, offset_x, dia_pos_y );
8371 // Point on left, move right
8372 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8380 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8384 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8385 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8386 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8387 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8389 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8391 if ( vtl->current_tpl ) {
8392 // get tp pixel position
8393 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8395 // Shift up<->down to try not to obscure the trackpoint.
8396 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8400 if ( vtl->current_tpl )
8401 if ( vtl->current_tp_track )
8402 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8403 /* set layer name and TP data */
8406 /***************************************************************************
8408 ***************************************************************************/
8410 /*** Utility data structures and functions ****/
8414 gint closest_x, closest_y;
8415 gboolean draw_images;
8416 gpointer *closest_wp_id;
8417 VikWaypoint *closest_wp;
8423 gint closest_x, closest_y;
8424 gpointer closest_track_id;
8425 VikTrackpoint *closest_tp;
8431 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8437 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8439 // If waypoint has an image then use the image size to select
8440 if ( params->draw_images && wp->image ) {
8441 gint slackx, slacky;
8442 slackx = wp->image_width / 2;
8443 slacky = wp->image_height / 2;
8445 if ( x <= params->x + slackx && x >= params->x - slackx
8446 && y <= params->y + slacky && y >= params->y - slacky ) {
8447 params->closest_wp_id = id;
8448 params->closest_wp = wp;
8449 params->closest_x = x;
8450 params->closest_y = y;
8453 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8454 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8455 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8457 params->closest_wp_id = id;
8458 params->closest_wp = wp;
8459 params->closest_x = x;
8460 params->closest_y = y;
8464 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8466 GList *tpl = t->trackpoints;
8472 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8478 tp = VIK_TRACKPOINT(tpl->data);
8480 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8482 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8483 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8484 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8486 params->closest_track_id = id;
8487 params->closest_tp = tp;
8488 params->closest_tpl = tpl;
8489 params->closest_x = x;
8490 params->closest_y = y;
8496 // ATM: Leave this as 'Track' only.
8497 // Not overly bothered about having a snap to route trackpoint capability
8498 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8500 TPSearchParams params;
8504 params.closest_track_id = NULL;
8505 params.closest_tp = NULL;
8506 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8507 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8508 return params.closest_tp;
8511 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8513 WPSearchParams params;
8517 params.draw_images = vtl->drawimages;
8518 params.closest_wp = NULL;
8519 params.closest_wp_id = NULL;
8520 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8521 return params.closest_wp;
8525 // Some forward declarations
8526 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8527 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8528 static void marker_end_move ( tool_ed_t *t );
8531 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8535 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8537 // Here always allow snapping back to the original location
8538 // this is useful when one decides not to move the thing afterall
8539 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8542 if ( event->state & GDK_CONTROL_MASK )
8544 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8546 new_coord = tp->coord;
8550 if ( event->state & GDK_SHIFT_MASK )
8552 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8554 new_coord = wp->coord;
8558 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8560 marker_moveto ( t, x, y );
8567 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8569 if ( t->holding && event->button == 1 )
8572 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8575 if ( event->state & GDK_CONTROL_MASK )
8577 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8579 new_coord = tp->coord;
8583 if ( event->state & GDK_SHIFT_MASK )
8585 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8587 new_coord = wp->coord;
8590 marker_end_move ( t );
8592 // Determine if working on a waypoint or a trackpoint
8593 if ( t->is_waypoint ) {
8594 // Update waypoint position
8595 vtl->current_wp->coord = new_coord;
8596 trw_layer_calculate_bounds_waypoints ( vtl );
8597 // Reset waypoint pointer
8598 vtl->current_wp = NULL;
8599 vtl->current_wp_id = NULL;
8602 if ( vtl->current_tpl ) {
8603 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8605 if ( vtl->current_tp_track )
8606 vik_track_calculate_bounds ( vtl->current_tp_track );
8609 if ( vtl->current_tp_track )
8610 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8611 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8615 vik_layer_emit_update ( VIK_LAYER(vtl) );
8622 Returns true if a waypoint or track is found near the requested event position for this particular layer
8623 The item found is automatically selected
8624 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8626 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8628 if ( event->button != 1 )
8631 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8634 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8638 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8640 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8642 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8643 WPSearchParams wp_params;
8644 wp_params.vvp = vvp;
8645 wp_params.x = event->x;
8646 wp_params.y = event->y;
8647 wp_params.draw_images = vtl->drawimages;
8648 wp_params.closest_wp_id = NULL;
8649 wp_params.closest_wp = NULL;
8651 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8653 if ( wp_params.closest_wp ) {
8656 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8658 // Too easy to move it so must be holding shift to start immediately moving it
8659 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8660 if ( event->state & GDK_SHIFT_MASK ||
8661 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8662 // Put into 'move buffer'
8663 // NB vvp & vw already set in tet
8664 tet->vtl = (gpointer)vtl;
8665 tet->is_waypoint = TRUE;
8667 marker_begin_move (tet, event->x, event->y);
8670 vtl->current_wp = wp_params.closest_wp;
8671 vtl->current_wp_id = wp_params.closest_wp_id;
8673 vik_layer_emit_update ( VIK_LAYER(vtl) );
8679 // Used for both track and route lists
8680 TPSearchParams tp_params;
8681 tp_params.vvp = vvp;
8682 tp_params.x = event->x;
8683 tp_params.y = event->y;
8684 tp_params.closest_track_id = NULL;
8685 tp_params.closest_tp = NULL;
8686 tp_params.closest_tpl = NULL;
8687 tp_params.bbox = bbox;
8689 if (vtl->tracks_visible) {
8690 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8692 if ( tp_params.closest_tp ) {
8694 // Always select + highlight the track
8695 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8697 tet->is_waypoint = FALSE;
8699 // Select the Trackpoint
8700 // Can move it immediately when control held or it's the previously selected tp
8701 if ( event->state & GDK_CONTROL_MASK ||
8702 vtl->current_tpl == tp_params.closest_tpl ) {
8703 // Put into 'move buffer'
8704 // NB vvp & vw already set in tet
8705 tet->vtl = (gpointer)vtl;
8706 marker_begin_move (tet, event->x, event->y);
8709 vtl->current_tpl = tp_params.closest_tpl;
8710 vtl->current_tp_id = tp_params.closest_track_id;
8711 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8713 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8716 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8718 vik_layer_emit_update ( VIK_LAYER(vtl) );
8723 // Try again for routes
8724 if (vtl->routes_visible) {
8725 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8727 if ( tp_params.closest_tp ) {
8729 // Always select + highlight the track
8730 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8732 tet->is_waypoint = FALSE;
8734 // Select the Trackpoint
8735 // Can move it immediately when control held or it's the previously selected tp
8736 if ( event->state & GDK_CONTROL_MASK ||
8737 vtl->current_tpl == tp_params.closest_tpl ) {
8738 // Put into 'move buffer'
8739 // NB vvp & vw already set in tet
8740 tet->vtl = (gpointer)vtl;
8741 marker_begin_move (tet, event->x, event->y);
8744 vtl->current_tpl = tp_params.closest_tpl;
8745 vtl->current_tp_id = tp_params.closest_track_id;
8746 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8748 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8751 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8753 vik_layer_emit_update ( VIK_LAYER(vtl) );
8758 /* these aren't the droids you're looking for */
8759 vtl->current_wp = NULL;
8760 vtl->current_wp_id = NULL;
8761 trw_layer_cancel_current_tp ( vtl, FALSE );
8764 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8769 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8771 if ( event->button != 3 )
8774 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8777 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8780 /* Post menu for the currently selected item */
8782 /* See if a track is selected */
8783 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8784 if ( track && track->visible ) {
8786 if ( track->name ) {
8788 if ( vtl->track_right_click_menu )
8789 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8791 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8798 if ( track->is_route )
8799 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8801 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8803 if ( trkf && udataU.uuid ) {
8806 if ( track->is_route )
8807 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8809 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8811 trw_layer_sublayer_add_menu_items ( vtl,
8812 vtl->track_right_click_menu,
8814 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8820 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8826 /* See if a waypoint is selected */
8827 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8828 if ( waypoint && waypoint->visible ) {
8829 if ( waypoint->name ) {
8831 if ( vtl->wp_right_click_menu )
8832 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8834 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8837 udata.wp = waypoint;
8840 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8842 if ( wpf && udata.uuid ) {
8843 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8845 trw_layer_sublayer_add_menu_items ( vtl,
8846 vtl->wp_right_click_menu,
8848 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8853 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8862 /* background drawing hook, to be passed the viewport */
8863 static gboolean tool_sync_done = TRUE;
8865 static gboolean tool_sync(gpointer data)
8867 VikViewport *vvp = data;
8868 gdk_threads_enter();
8869 vik_viewport_sync(vvp);
8870 tool_sync_done = TRUE;
8871 gdk_threads_leave();
8875 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8878 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8879 gdk_gc_set_function ( t->gc, GDK_INVERT );
8880 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8881 vik_viewport_sync(t->vvp);
8886 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8888 VikViewport *vvp = t->vvp;
8889 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8890 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8894 if (tool_sync_done) {
8895 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8896 tool_sync_done = FALSE;
8900 static void marker_end_move ( tool_ed_t *t )
8902 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8903 g_object_unref ( t->gc );
8907 /*** Edit waypoint ****/
8909 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8911 tool_ed_t *t = g_new(tool_ed_t, 1);
8917 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8922 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8924 WPSearchParams params;
8925 tool_ed_t *t = data;
8926 VikViewport *vvp = t->vvp;
8928 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8935 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8938 if ( vtl->current_wp && vtl->current_wp->visible )
8940 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8942 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8944 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8945 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8947 if ( event->button == 3 )
8948 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8950 marker_begin_move(t, event->x, event->y);
8957 params.x = event->x;
8958 params.y = event->y;
8959 params.draw_images = vtl->drawimages;
8960 params.closest_wp_id = NULL;
8961 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8962 params.closest_wp = NULL;
8963 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8964 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8966 // how do we get here?
8967 marker_begin_move(t, event->x, event->y);
8968 g_critical("shouldn't be here");
8971 else if ( params.closest_wp )
8973 if ( event->button == 3 )
8974 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8976 vtl->waypoint_rightclick = FALSE;
8978 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8980 vtl->current_wp = params.closest_wp;
8981 vtl->current_wp_id = params.closest_wp_id;
8983 /* could make it so don't update if old WP is off screen and new is null but oh well */
8984 vik_layer_emit_update ( VIK_LAYER(vtl) );
8988 vtl->current_wp = NULL;
8989 vtl->current_wp_id = NULL;
8990 vtl->waypoint_rightclick = FALSE;
8991 vik_layer_emit_update ( VIK_LAYER(vtl) );
8995 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8997 tool_ed_t *t = data;
8998 VikViewport *vvp = t->vvp;
9000 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9005 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9008 if ( event->state & GDK_CONTROL_MASK )
9010 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9012 new_coord = tp->coord;
9016 if ( event->state & GDK_SHIFT_MASK )
9018 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9019 if ( wp && wp != vtl->current_wp )
9020 new_coord = wp->coord;
9025 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9027 marker_moveto ( t, x, y );
9034 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9036 tool_ed_t *t = data;
9037 VikViewport *vvp = t->vvp;
9039 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9042 if ( t->holding && event->button == 1 )
9045 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9048 if ( event->state & GDK_CONTROL_MASK )
9050 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9052 new_coord = tp->coord;
9056 if ( event->state & GDK_SHIFT_MASK )
9058 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9059 if ( wp && wp != vtl->current_wp )
9060 new_coord = wp->coord;
9063 marker_end_move ( t );
9065 vtl->current_wp->coord = new_coord;
9067 trw_layer_calculate_bounds_waypoints ( vtl );
9068 vik_layer_emit_update ( VIK_LAYER(vtl) );
9071 /* PUT IN RIGHT PLACE!!! */
9072 if ( event->button == 3 && vtl->waypoint_rightclick )
9074 if ( vtl->wp_right_click_menu )
9075 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9076 if ( vtl->current_wp ) {
9077 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9078 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 );
9079 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9081 vtl->waypoint_rightclick = FALSE;
9086 /*** New track ****/
9088 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9095 GdkDrawable *drawable;
9101 * Draw specified pixmap
9103 static gboolean draw_sync ( gpointer data )
9105 draw_sync_t *ds = (draw_sync_t*) data;
9106 // Sometimes don't want to draw
9107 // normally because another update has taken precedent such as panning the display
9108 // which means this pixmap is no longer valid
9109 if ( ds->vtl->draw_sync_do ) {
9110 gdk_threads_enter();
9111 gdk_draw_drawable (ds->drawable,
9114 0, 0, 0, 0, -1, -1);
9115 ds->vtl->draw_sync_done = TRUE;
9116 gdk_threads_leave();
9122 static gchar* distance_string (gdouble distance)
9126 /* draw label with distance */
9127 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9128 switch (dist_units) {
9129 case VIK_UNITS_DISTANCE_MILES:
9130 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9131 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9132 } else if (distance < 1609.4) {
9133 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9135 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9139 // VIK_UNITS_DISTANCE_KILOMETRES
9140 if (distance >= 1000 && distance < 100000) {
9141 g_sprintf(str, "%3.2f km", distance/1000.0);
9142 } else if (distance < 1000) {
9143 g_sprintf(str, "%d m", (int)distance);
9145 g_sprintf(str, "%d km", (int)distance/1000);
9149 return g_strdup (str);
9153 * Actually set the message in statusbar
9155 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9157 // Only show elevation data when track has some elevation properties
9158 gchar str_gain_loss[64];
9159 str_gain_loss[0] = '\0';
9160 gchar str_last_step[64];
9161 str_last_step[0] = '\0';
9162 gchar *str_total = distance_string (distance);
9164 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9165 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9166 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9168 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9171 if ( last_step > 0 ) {
9172 gchar *tmp = distance_string (last_step);
9173 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9177 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9179 // Write with full gain/loss information
9180 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9181 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9183 g_free ( str_total );
9187 * Figure out what information should be set in the statusbar and then write it
9189 static void update_statusbar ( VikTrwLayer *vtl )
9191 // Get elevation data
9192 gdouble elev_gain, elev_loss;
9193 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9195 /* Find out actual distance of current track */
9196 gdouble distance = vik_track_get_length (vtl->current_track);
9198 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9202 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9204 /* if we haven't sync'ed yet, we don't have time to do more. */
9205 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9206 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9207 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9209 static GdkPixmap *pixmap = NULL;
9211 // Need to check in case window has been resized
9212 w1 = vik_viewport_get_width(vvp);
9213 h1 = vik_viewport_get_height(vvp);
9215 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9217 gdk_drawable_get_size (pixmap, &w2, &h2);
9218 if (w1 != w2 || h1 != h2) {
9219 g_object_unref ( G_OBJECT ( pixmap ) );
9220 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9223 // Reset to background
9224 gdk_draw_drawable (pixmap,
9225 vtl->current_track_newpoint_gc,
9226 vik_viewport_get_pixmap(vvp),
9227 0, 0, 0, 0, -1, -1);
9229 draw_sync_t *passalong;
9232 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9234 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9235 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9236 // thus when we come to reset to the background it would include what we have already drawn!!
9237 gdk_draw_line ( pixmap,
9238 vtl->current_track_newpoint_gc,
9239 x1, y1, event->x, event->y );
9240 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9242 /* Find out actual distance of current track */
9243 gdouble distance = vik_track_get_length (vtl->current_track);
9245 // Now add distance to where the pointer is //
9248 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9249 vik_coord_to_latlon ( &coord, &ll );
9250 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9251 distance = distance + last_step;
9253 // Get elevation data
9254 gdouble elev_gain, elev_loss;
9255 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9257 // Adjust elevation data (if available) for the current pointer position
9259 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9260 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9261 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9262 // Adjust elevation of last track point
9263 if ( elev_new > last_tpt->altitude )
9265 elev_gain += elev_new - last_tpt->altitude;
9268 elev_loss += last_tpt->altitude - elev_new;
9273 // Display of the distance 'tooltip' during track creation is controlled by a preference
9275 if ( a_vik_get_create_track_tooltip() ) {
9277 gchar *str = distance_string (distance);
9279 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9280 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9281 pango_layout_set_text (pl, str, -1);
9283 pango_layout_get_pixel_size ( pl, &wd, &hd );
9286 // offset from cursor a bit depending on font size
9290 // Create a background block to make the text easier to read over the background map
9291 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9292 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9293 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9295 g_object_unref ( G_OBJECT ( pl ) );
9296 g_object_unref ( G_OBJECT ( background_block_gc ) );
9300 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9301 passalong->vtl = vtl;
9302 passalong->pixmap = pixmap;
9303 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9304 passalong->gc = vtl->current_track_newpoint_gc;
9308 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9310 // Update statusbar with full gain/loss information
9311 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9313 // draw pixmap when we have time to
9314 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9315 vtl->draw_sync_done = FALSE;
9316 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9318 return VIK_LAYER_TOOL_ACK;
9321 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9323 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9324 vtl->current_track = NULL;
9325 vik_layer_emit_update ( VIK_LAYER(vtl) );
9327 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9329 if ( vtl->current_track->trackpoints )
9331 GList *last = g_list_last(vtl->current_track->trackpoints);
9332 g_free ( last->data );
9333 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9336 update_statusbar ( vtl );
9338 vik_layer_emit_update ( VIK_LAYER(vtl) );
9345 * Common function to handle trackpoint button requests on either a route or a track
9346 * . enables adding a point via normal click
9347 * . enables removal of last point via right click
9348 * . finishing of the track or route via double clicking
9350 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9354 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9357 if ( event->button == 2 ) {
9358 // As the display is panning, the new track pixmap is now invalid so don't draw it
9359 // otherwise this drawing done results in flickering back to an old image
9360 vtl->draw_sync_do = FALSE;
9364 if ( event->button == 3 )
9366 if ( !vtl->current_track )
9369 if ( vtl->current_track->trackpoints )
9371 GList *last = g_list_last(vtl->current_track->trackpoints);
9372 g_free ( last->data );
9373 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9375 vik_track_calculate_bounds ( vtl->current_track );
9376 update_statusbar ( vtl );
9378 vik_layer_emit_update ( VIK_LAYER(vtl) );
9382 if ( event->type == GDK_2BUTTON_PRESS )
9384 /* subtract last (duplicate from double click) tp then end */
9385 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9387 GList *last = g_list_last(vtl->current_track->trackpoints);
9388 g_free ( last->data );
9389 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9390 /* undo last, then end */
9391 vtl->current_track = NULL;
9393 vik_layer_emit_update ( VIK_LAYER(vtl) );
9397 tp = vik_trackpoint_new();
9398 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9400 /* snap to other TP */
9401 if ( event->state & GDK_CONTROL_MASK )
9403 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9405 tp->coord = other_tp->coord;
9408 tp->newsegment = FALSE;
9409 tp->has_timestamp = FALSE;
9412 if ( vtl->current_track ) {
9413 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9414 /* Auto attempt to get elevation from DEM data (if it's available) */
9415 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9418 vtl->ct_x1 = vtl->ct_x2;
9419 vtl->ct_y1 = vtl->ct_y2;
9420 vtl->ct_x2 = event->x;
9421 vtl->ct_y2 = event->y;
9423 vik_layer_emit_update ( VIK_LAYER(vtl) );
9427 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9429 // ----------------------------------------------------- if current is a route - switch to new track
9430 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9432 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9433 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9435 new_track_create_common ( vtl, name );
9441 return tool_new_track_or_route_click ( vtl, event, vvp );
9444 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9446 if ( event->button == 2 ) {
9447 // Pan moving ended - enable potential point drawing again
9448 vtl->draw_sync_do = TRUE;
9449 vtl->draw_sync_done = TRUE;
9453 /*** New route ****/
9455 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9460 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9462 // -------------------------- if current is a track - switch to new route
9463 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9465 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9466 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9467 new_route_create_common ( vtl, name );
9473 return tool_new_track_or_route_click ( vtl, event, vvp );
9476 /*** New waypoint ****/
9478 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9483 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9486 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9488 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9489 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9490 trw_layer_calculate_bounds_waypoints ( vtl );
9491 vik_layer_emit_update ( VIK_LAYER(vtl) );
9497 /*** Edit trackpoint ****/
9499 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9501 tool_ed_t *t = g_new(tool_ed_t, 1);
9507 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9512 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9514 tool_ed_t *t = data;
9515 VikViewport *vvp = t->vvp;
9516 TPSearchParams params;
9517 /* OUTDATED DOCUMENTATION:
9518 find 5 pixel range on each side. then put these UTM, and a pointer
9519 to the winning track name (and maybe the winning track itself), and a
9520 pointer to the winning trackpoint, inside an array or struct. pass
9521 this along, do a foreach on the tracks which will do a foreach on the
9524 params.x = event->x;
9525 params.y = event->y;
9526 params.closest_track_id = NULL;
9527 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9528 params.closest_tp = NULL;
9529 params.closest_tpl = NULL;
9530 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9532 if ( event->button != 1 )
9535 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9538 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9541 if ( vtl->current_tpl )
9543 /* 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.) */
9544 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9545 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9550 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9552 if ( current_tr->visible &&
9553 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9554 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9555 marker_begin_move ( t, event->x, event->y );
9561 if ( vtl->tracks_visible )
9562 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9564 if ( params.closest_tp )
9566 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9567 vtl->current_tpl = params.closest_tpl;
9568 vtl->current_tp_id = params.closest_track_id;
9569 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9570 trw_layer_tpwin_init ( vtl );
9571 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9572 vik_layer_emit_update ( VIK_LAYER(vtl) );
9576 if ( vtl->routes_visible )
9577 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9579 if ( params.closest_tp )
9581 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9582 vtl->current_tpl = params.closest_tpl;
9583 vtl->current_tp_id = params.closest_track_id;
9584 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9585 trw_layer_tpwin_init ( vtl );
9586 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9587 vik_layer_emit_update ( VIK_LAYER(vtl) );
9591 /* these aren't the droids you're looking for */
9595 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9597 tool_ed_t *t = data;
9598 VikViewport *vvp = t->vvp;
9600 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9606 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9609 if ( event->state & GDK_CONTROL_MASK )
9611 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9612 if ( tp && tp != vtl->current_tpl->data )
9613 new_coord = tp->coord;
9615 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9618 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9619 marker_moveto ( t, x, y );
9627 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9629 tool_ed_t *t = data;
9630 VikViewport *vvp = t->vvp;
9632 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9634 if ( event->button != 1)
9639 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9642 if ( event->state & GDK_CONTROL_MASK )
9644 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9645 if ( tp && tp != vtl->current_tpl->data )
9646 new_coord = tp->coord;
9649 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9650 if ( vtl->current_tp_track )
9651 vik_track_calculate_bounds ( vtl->current_tp_track );
9653 marker_end_move ( t );
9655 /* diff dist is diff from orig */
9657 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9659 vik_layer_emit_update ( VIK_LAYER(vtl) );
9666 /*** Route Finder ***/
9667 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9672 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9675 if ( !vtl ) return FALSE;
9676 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9677 if ( event->button == 3 && vtl->route_finder_current_track ) {
9679 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9681 vtl->route_finder_coord = *new_end;
9683 vik_layer_emit_update ( VIK_LAYER(vtl) );
9684 /* remove last ' to:...' */
9685 if ( vtl->route_finder_current_track->comment ) {
9686 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9687 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9688 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9689 last_to - vtl->route_finder_current_track->comment - 1);
9690 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9695 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9696 struct LatLon start, end;
9698 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9699 vik_coord_to_latlon ( &(tmp), &end );
9700 vtl->route_finder_coord = tmp; /* for continuations */
9702 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9703 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9704 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9706 vtl->route_finder_check_added_track = TRUE;
9707 vtl->route_finder_started = FALSE;
9710 vik_routing_default_find ( vtl, start, end);
9712 /* see if anything was done -- a track was added or appended to */
9713 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9714 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 ) );
9715 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9716 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9717 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9718 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9721 if ( vtl->route_finder_added_track )
9722 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9724 vtl->route_finder_added_track = NULL;
9725 vtl->route_finder_check_added_track = FALSE;
9726 vtl->route_finder_append = FALSE;
9728 vik_layer_emit_update ( VIK_LAYER(vtl) );
9730 vtl->route_finder_started = TRUE;
9731 vtl->route_finder_coord = tmp;
9732 vtl->route_finder_current_track = NULL;
9737 /*** Show picture ****/
9739 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9744 /* Params are: vvp, event, last match found or NULL */
9745 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9747 if ( wp->image && wp->visible )
9749 gint x, y, slackx, slacky;
9750 GdkEventButton *event = (GdkEventButton *) params[1];
9752 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9753 slackx = wp->image_width / 2;
9754 slacky = wp->image_height / 2;
9755 if ( x <= event->x + slackx && x >= event->x - slackx
9756 && y <= event->y + slacky && y >= event->y - slacky )
9758 params[2] = wp->image; /* we've found a match. however continue searching
9759 * since we want to find the last match -- that
9760 * is, the match that was drawn last. */
9765 static void trw_layer_show_picture ( gpointer pass_along[6] )
9767 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9769 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9772 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9773 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9774 g_free ( quoted_file );
9775 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9777 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
9778 g_error_free ( err );
9781 #endif /* WINDOWS */
9784 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9786 gpointer params[3] = { vvp, event, NULL };
9787 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9789 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9792 static gpointer pass_along[6];
9793 pass_along[0] = vtl;
9794 pass_along[5] = params[2];
9795 trw_layer_show_picture ( pass_along );
9796 return TRUE; /* found a match */
9799 return FALSE; /* go through other layers, searching for a match */
9802 /***************************************************************************
9804 ***************************************************************************/
9807 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9809 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9810 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9813 /* Structure for thumbnail creating data used in the background thread */
9815 VikTrwLayer *vtl; // Layer needed for redrawing
9816 GSList *pics; // Image list
9817 } thumbnail_create_thread_data;
9819 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9821 guint total = g_slist_length(tctd->pics), done = 0;
9822 while ( tctd->pics )
9824 a_thumbnails_create ( (gchar *) tctd->pics->data );
9825 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9827 return -1; /* Abort thread */
9829 tctd->pics = tctd->pics->next;
9832 // Redraw to show the thumbnails as they are now created
9833 if ( IS_VIK_LAYER(tctd->vtl) )
9834 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9839 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9841 while ( tctd->pics )
9843 g_free ( tctd->pics->data );
9844 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9849 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9851 if ( ! vtl->has_verified_thumbnails )
9853 GSList *pics = NULL;
9854 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9857 gint len = g_slist_length ( pics );
9858 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9859 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9862 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9864 (vik_thr_func) create_thumbnails_thread,
9866 (vik_thr_free_func) thumbnail_create_thread_free,
9874 static const gchar* my_track_colors ( gint ii )
9876 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9888 // Fast and reliable way of returning a colour
9889 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9892 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9894 GHashTableIter iter;
9895 gpointer key, value;
9899 g_hash_table_iter_init ( &iter, vtl->tracks );
9901 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9903 // Tracks get a random spread of colours if not already assigned
9904 if ( ! VIK_TRACK(value)->has_color ) {
9905 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9906 VIK_TRACK(value)->color = vtl->track_color;
9908 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9910 VIK_TRACK(value)->has_color = TRUE;
9913 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9916 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9922 g_hash_table_iter_init ( &iter, vtl->routes );
9924 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9926 // Routes get an intermix of reds
9927 if ( ! VIK_TRACK(value)->has_color ) {
9929 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9931 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9932 VIK_TRACK(value)->has_color = TRUE;
9935 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9942 * (Re)Calculate the bounds of the waypoints in this layer,
9943 * This should be called whenever waypoints are changed
9945 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9947 struct LatLon topleft = { 0.0, 0.0 };
9948 struct LatLon bottomright = { 0.0, 0.0 };
9951 GHashTableIter iter;
9952 gpointer key, value;
9954 g_hash_table_iter_init ( &iter, vtl->waypoints );
9956 // Set bounds to first point
9957 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9958 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9959 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9962 // Ensure there is another point...
9963 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9965 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9967 // See if this point increases the bounds.
9968 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9970 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9971 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9972 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9973 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9977 vtl->waypoints_bbox.north = topleft.lat;
9978 vtl->waypoints_bbox.east = bottomright.lon;
9979 vtl->waypoints_bbox.south = bottomright.lat;
9980 vtl->waypoints_bbox.west = topleft.lon;
9983 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9985 vik_track_calculate_bounds ( trk );
9988 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9990 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9991 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9994 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9996 if ( ! VIK_LAYER(vtl)->vt )
9999 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10000 if ( g_hash_table_size (vtl->tracks) > 1 )
10001 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10003 if ( g_hash_table_size (vtl->routes) > 1 )
10004 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10006 if ( g_hash_table_size (vtl->waypoints) > 1 )
10007 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10010 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10012 if ( VIK_LAYER(vtl)->realized )
10013 trw_layer_verify_thumbnails ( vtl, vvp );
10014 trw_layer_track_alloc_colors ( vtl );
10016 trw_layer_calculate_bounds_waypoints ( vtl );
10017 trw_layer_calculate_bounds_tracks ( vtl );
10019 // Apply treeview sort after loading all the tracks for this layer
10020 // (rather than sorted insert on each individual track additional)
10021 // and after subsequent changes to the properties as the specified order may have changed.
10022 // since the sorting of a treeview section is now very quick
10023 // NB sorting is also performed after every name change as well to maintain the list order
10024 trw_layer_sort_all ( vtl );
10027 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10029 return vtl->coord_mode;
10033 * Uniquify the whole layer
10034 * Also requires the layers panel as the names shown there need updating too
10035 * Returns whether the operation was successful or not
10037 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10039 if ( vtl && vlp ) {
10040 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10041 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10042 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10048 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10050 vik_coord_convert ( &(wp->coord), *dest_mode );
10053 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10055 vik_track_convert ( tr, *dest_mode );
10058 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10060 if ( vtl->coord_mode != dest_mode )
10062 vtl->coord_mode = dest_mode;
10063 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10064 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10065 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10069 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10071 vtl->menu_selection = selection;
10074 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10076 return (vtl->menu_selection);
10079 /* ----------- Downloading maps along tracks --------------- */
10081 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10083 /* TODO: calculating based on current size of viewport */
10084 const gdouble w_at_zoom_0_125 = 0.0013;
10085 const gdouble h_at_zoom_0_125 = 0.0011;
10086 gdouble zoom_factor = zoom_level/0.125;
10088 wh->lat = h_at_zoom_0_125 * zoom_factor;
10089 wh->lon = w_at_zoom_0_125 * zoom_factor;
10091 return 0; /* all OK */
10094 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10096 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10097 (dist->lat >= ABS(to->north_south - from->north_south)))
10100 VikCoord *coord = g_malloc(sizeof(VikCoord));
10101 coord->mode = VIK_COORD_LATLON;
10103 if (ABS(gradient) < 1) {
10104 if (from->east_west > to->east_west)
10105 coord->east_west = from->east_west - dist->lon;
10107 coord->east_west = from->east_west + dist->lon;
10108 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10110 if (from->north_south > to->north_south)
10111 coord->north_south = from->north_south - dist->lat;
10113 coord->north_south = from->north_south + dist->lat;
10114 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10120 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10122 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10123 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10125 VikCoord *next = from;
10127 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10129 list = g_list_prepend(list, next);
10135 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10137 typedef struct _Rect {
10142 #define GLRECT(iter) ((Rect *)((iter)->data))
10145 GList *rects_to_download = NULL;
10148 if (get_download_area_width(vvp, zoom_level, &wh))
10151 GList *iter = tr->trackpoints;
10155 gboolean new_map = TRUE;
10156 VikCoord *cur_coord, tl, br;
10159 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10161 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10162 rect = g_malloc(sizeof(Rect));
10165 rect->center = *cur_coord;
10166 rects_to_download = g_list_prepend(rects_to_download, rect);
10171 gboolean found = FALSE;
10172 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10173 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10184 GList *fillins = NULL;
10185 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10186 /* seems that ATM the function get_next_coord works only for LATLON */
10187 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10188 /* fill-ins for far apart points */
10189 GList *cur_rect, *next_rect;
10190 for (cur_rect = rects_to_download;
10191 (next_rect = cur_rect->next) != NULL;
10192 cur_rect = cur_rect->next) {
10193 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10194 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10195 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10199 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10202 GList *iter = fillins;
10204 cur_coord = (VikCoord *)(iter->data);
10205 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10206 rect = g_malloc(sizeof(Rect));
10209 rect->center = *cur_coord;
10210 rects_to_download = g_list_prepend(rects_to_download, rect);
10215 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10216 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10220 for (iter = fillins; iter; iter = iter->next)
10221 g_free(iter->data);
10222 g_list_free(fillins);
10224 if (rects_to_download) {
10225 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10226 g_free(rect_iter->data);
10227 g_list_free(rects_to_download);
10231 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10235 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10236 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10237 gint selected_zoom, default_zoom;
10239 VikTrwLayer *vtl = pass_along[0];
10240 VikLayersPanel *vlp = pass_along[1];
10242 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10243 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10245 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10249 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10251 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10252 int num_maps = g_list_length(vmls);
10255 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10259 // Convert from list of vmls to list of names. Allowing the user to select one of them
10260 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10261 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10263 gchar **np = map_names;
10264 VikMapsLayer **lp = map_layers;
10266 for (i = 0; i < num_maps; i++) {
10267 vml = (VikMapsLayer *)(vmls->data);
10269 *np++ = vik_maps_layer_get_map_label(vml);
10272 // Mark end of the array lists
10276 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10277 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10278 if (cur_zoom == zoom_vals[default_zoom])
10281 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10283 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10286 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10289 for (i = 0; i < num_maps; i++)
10290 g_free(map_names[i]);
10292 g_free(map_layers);
10298 /**** lowest waypoint number calculation ***/
10299 static gint highest_wp_number_name_to_number(const gchar *name) {
10300 if ( strlen(name) == 3 ) {
10301 int n = atoi(name);
10302 if ( n < 100 && name[0] != '0' )
10304 if ( n < 10 && name[0] != '0' )
10312 static void highest_wp_number_reset(VikTrwLayer *vtl)
10314 vtl->highest_wp_number = -1;
10317 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10319 /* if is bigger that top, add it */
10320 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10321 if ( new_wp_num > vtl->highest_wp_number )
10322 vtl->highest_wp_number = new_wp_num;
10325 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10327 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10328 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10329 if ( vtl->highest_wp_number == old_wp_num ) {
10331 vtl->highest_wp_number--;
10333 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10334 /* search down until we find something that *does* exist */
10336 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10337 vtl->highest_wp_number--;
10338 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10343 /* get lowest unused number */
10344 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10347 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10349 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10350 return g_strdup(buf);
10354 * trw_layer_create_track_list_both:
10356 * Create the latest list of tracks and routes
10358 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10360 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10361 GList *tracks = NULL;
10362 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10363 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10365 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10368 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] )
10370 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
10372 gchar *title = NULL;
10373 if ( GPOINTER_TO_INT(pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10374 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10376 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10378 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), pass_along[2], trw_layer_create_track_list, FALSE );
10382 static void trw_layer_track_list_dialog ( gpointer lav[2] )
10384 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10385 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10387 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10388 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10392 static void trw_layer_waypoint_list_dialog ( gpointer lav[2] )
10394 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10395 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10397 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10398 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );