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 //
3412 static void trw_layer_acquire ( gpointer lav[2], VikDataSourceInterface *datasource )
3414 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3415 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3416 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3417 VikViewport *vvp = vik_window_viewport(vw);
3419 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3423 * Acquire into this TRW Layer straight from GPS Device
3425 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3427 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3428 trw_layer_acquire ( lav, &vik_datasource_gps_interface );
3432 * Acquire into this TRW Layer from Directions
3434 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3436 trw_layer_acquire ( lav, &vik_datasource_routing_interface );
3440 * Acquire into this TRW Layer from an entered URL
3442 static void trw_layer_acquire_url_cb ( gpointer lav[2] )
3444 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3445 trw_layer_acquire ( lav, &vik_datasource_url_interface );
3448 #ifdef VIK_CONFIG_OPENSTREETMAP
3450 * Acquire into this TRW Layer from OSM
3452 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3454 trw_layer_acquire ( lav, &vik_datasource_osm_interface );
3458 * Acquire into this TRW Layer from OSM for 'My' Traces
3460 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3462 trw_layer_acquire ( lav, &vik_datasource_osm_my_traces_interface );
3466 #ifdef VIK_CONFIG_GEOCACHES
3468 * Acquire into this TRW Layer from Geocaching.com
3470 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3472 trw_layer_acquire ( lav, &vik_datasource_gc_interface );
3476 #ifdef VIK_CONFIG_GEOTAG
3478 * Acquire into this TRW Layer from images
3480 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3482 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3484 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3485 trw_layer_acquire ( lav, &vik_datasource_geotag_interface );
3487 // Reverify thumbnails as they may have changed
3488 vtl->has_verified_thumbnails = FALSE;
3489 trw_layer_verify_thumbnails ( vtl, NULL );
3493 static void trw_layer_gps_upload ( gpointer lav[2] )
3495 gpointer pass_along[6];
3496 pass_along[0] = lav[0];
3497 pass_along[1] = lav[1];
3498 pass_along[2] = NULL; // No track - operate on the layer
3499 pass_along[3] = NULL;
3500 pass_along[4] = NULL;
3501 pass_along[5] = NULL;
3503 trw_layer_gps_upload_any ( pass_along );
3507 * If pass_along[3] is defined that this will upload just that track
3509 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3511 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3512 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3514 // May not actually get a track here as pass_along[2&3] can be null
3515 VikTrack *track = NULL;
3516 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3517 gboolean xfer_all = FALSE;
3519 if ( pass_along[2] ) {
3521 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3522 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3525 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3526 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3529 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3532 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3536 else if ( !pass_along[4] )
3537 xfer_all = TRUE; // i.e. whole layer
3539 if (track && !track->visible) {
3540 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3544 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3545 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3546 GTK_DIALOG_DESTROY_WITH_PARENT,
3548 GTK_RESPONSE_ACCEPT,
3550 GTK_RESPONSE_REJECT,
3553 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3554 GtkWidget *response_w = NULL;
3555 #if GTK_CHECK_VERSION (2, 20, 0)
3556 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3560 gtk_widget_grab_focus ( response_w );
3562 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3564 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3565 datasource_gps_clean_up ( dgs );
3566 gtk_widget_destroy ( dialog );
3570 // Get info from reused datasource dialog widgets
3571 gchar* protocol = datasource_gps_get_protocol ( dgs );
3572 gchar* port = datasource_gps_get_descriptor ( dgs );
3573 // NB don't free the above strings as they're references to values held elsewhere
3574 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3575 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3576 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3577 gboolean turn_off = datasource_gps_get_off ( dgs );
3579 gtk_widget_destroy ( dialog );
3581 // When called from the viewport - work the corresponding layerspanel:
3583 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3586 // Apply settings to transfer to the GPS device
3593 vik_layers_panel_get_viewport (vlp),
3602 * Acquire into this TRW Layer from any GPS Babel supported file
3604 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3606 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3607 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3608 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3609 VikViewport *vvp = vik_window_viewport(vw);
3611 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3614 static void trw_layer_new_wp ( gpointer lav[2] )
3616 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3617 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3618 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3619 instead return true if you want to update. */
3620 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 ) {
3621 trw_layer_calculate_bounds_waypoints ( vtl );
3622 vik_layers_panel_emit_update ( vlp );
3626 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3628 vtl->current_track = vik_track_new();
3629 vik_track_set_defaults ( vtl->current_track );
3630 vtl->current_track->visible = TRUE;
3631 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3632 // Create track with the preferred colour from the layer properties
3633 vtl->current_track->color = vtl->track_color;
3635 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3636 vtl->current_track->has_color = TRUE;
3637 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3640 static void trw_layer_new_track ( gpointer lav[2] )
3642 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3644 if ( ! vtl->current_track ) {
3645 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3646 new_track_create_common ( vtl, name );
3649 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3653 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3655 vtl->current_track = vik_track_new();
3656 vik_track_set_defaults ( vtl->current_track );
3657 vtl->current_track->visible = TRUE;
3658 vtl->current_track->is_route = TRUE;
3659 // By default make all routes red
3660 vtl->current_track->has_color = TRUE;
3661 gdk_color_parse ( "red", &vtl->current_track->color );
3662 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3665 static void trw_layer_new_route ( gpointer lav[2] )
3667 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3669 if ( ! vtl->current_track ) {
3670 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3671 new_route_create_common ( vtl, name );
3673 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3677 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3679 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3680 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3682 if ( g_hash_table_size (vtl->routes) > 0 ) {
3683 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3684 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3685 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3686 vik_layers_panel_emit_update ( vlp );
3691 static void trw_layer_finish_track ( gpointer lav[2] )
3693 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3694 vtl->current_track = NULL;
3695 vik_layer_emit_update ( VIK_LAYER(vtl) );
3698 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3700 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3701 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3703 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3704 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3705 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3706 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3707 vik_layers_panel_emit_update ( vlp );
3711 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3713 /* NB do not care if wp is visible or not */
3714 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3717 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3719 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3720 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3722 /* Only 1 waypoint - jump straight to it */
3723 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3724 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3725 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3727 /* If at least 2 waypoints - find center and then zoom to fit */
3728 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3730 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3731 maxmin[0].lat = vtl->waypoints_bbox.north;
3732 maxmin[1].lat = vtl->waypoints_bbox.south;
3733 maxmin[0].lon = vtl->waypoints_bbox.east;
3734 maxmin[1].lon = vtl->waypoints_bbox.west;
3735 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3738 vik_layers_panel_emit_update ( vlp );
3741 void trw_layer_osm_traces_upload_cb ( gpointer layer_and_vlp[2] )
3743 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(layer_and_vlp[0]), NULL);
3746 void trw_layer_osm_traces_upload_track_cb ( gpointer pass_along[8] )
3748 if ( pass_along[7] ) {
3749 VikTrack *trk = VIK_TRACK(pass_along[7]);
3750 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(pass_along[0]), trk);
3754 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3756 static gpointer pass_along[2];
3758 GtkWidget *export_submenu;
3759 pass_along[0] = vtl;
3760 pass_along[1] = vlp;
3762 item = gtk_menu_item_new();
3763 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3764 gtk_widget_show ( item );
3766 if ( vtl->current_track ) {
3767 if ( vtl->current_track->is_route )
3768 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3770 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3771 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3772 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3773 gtk_widget_show ( item );
3776 item = gtk_menu_item_new ();
3777 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3778 gtk_widget_show ( item );
3781 /* Now with icons */
3782 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3783 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3784 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3785 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3786 gtk_widget_show ( item );
3788 GtkWidget *view_submenu = gtk_menu_new();
3789 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3790 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3791 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3792 gtk_widget_show ( item );
3793 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3795 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3797 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3798 gtk_widget_show ( item );
3800 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3801 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3802 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3803 gtk_widget_show ( item );
3805 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3806 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3807 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3808 gtk_widget_show ( item );
3810 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3811 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3812 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3813 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3814 gtk_widget_show ( item );
3816 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3817 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3818 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3819 gtk_widget_show ( item );
3821 export_submenu = gtk_menu_new ();
3822 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3824 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3825 gtk_widget_show ( item );
3826 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3828 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3830 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3831 gtk_widget_show ( item );
3833 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3834 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3835 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3836 gtk_widget_show ( item );
3838 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3839 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), 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 _KML...") );
3844 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3845 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3846 gtk_widget_show ( item );
3848 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3849 item = gtk_menu_item_new_with_mnemonic ( external1 );
3850 g_free ( external1 );
3851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3852 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3853 gtk_widget_show ( item );
3855 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
3856 item = gtk_menu_item_new_with_mnemonic ( external2 );
3857 g_free ( external2 );
3858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3859 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3860 gtk_widget_show ( item );
3862 GtkWidget *new_submenu = gtk_menu_new();
3863 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3864 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3865 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3866 gtk_widget_show(item);
3867 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3869 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3870 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3871 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3872 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3873 gtk_widget_show ( item );
3875 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3876 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3877 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3878 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3879 gtk_widget_show ( item );
3880 // Make it available only when a new track *not* already in progress
3881 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3883 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3884 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3885 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3886 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3887 gtk_widget_show ( item );
3888 // Make it available only when a new track *not* already in progress
3889 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3891 #ifdef VIK_CONFIG_GEOTAG
3892 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3894 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3895 gtk_widget_show ( item );
3898 GtkWidget *acquire_submenu = gtk_menu_new ();
3899 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3901 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3902 gtk_widget_show ( item );
3903 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3905 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3906 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3907 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3908 gtk_widget_show ( item );
3910 /* FIXME: only add menu when at least a routing engine has support for Directions */
3911 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3912 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3913 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3914 gtk_widget_show ( item );
3916 #ifdef VIK_CONFIG_OPENSTREETMAP
3917 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3919 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3920 gtk_widget_show ( item );
3922 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3924 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3925 gtk_widget_show ( item );
3928 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
3929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
3930 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3931 gtk_widget_show ( item );
3933 #ifdef VIK_CONFIG_GEONAMES
3934 GtkWidget *wikipedia_submenu = gtk_menu_new();
3935 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3936 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3937 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3938 gtk_widget_show(item);
3939 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3941 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3942 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3944 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3945 gtk_widget_show ( item );
3947 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3948 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3949 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3950 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3951 gtk_widget_show ( item );
3954 #ifdef VIK_CONFIG_GEOCACHES
3955 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3957 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3958 gtk_widget_show ( item );
3961 #ifdef VIK_CONFIG_GEOTAG
3962 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3964 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3965 gtk_widget_show ( item );
3968 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3970 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3971 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
3972 gtk_widget_show ( item );
3974 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3976 GtkWidget *upload_submenu = gtk_menu_new ();
3977 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3979 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3980 gtk_widget_show ( item );
3981 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3983 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3986 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3987 gtk_widget_show ( item );
3989 #ifdef VIK_CONFIG_OPENSTREETMAP
3990 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3991 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3992 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
3993 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3994 gtk_widget_show ( item );
3997 GtkWidget *delete_submenu = gtk_menu_new ();
3998 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3999 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4000 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4001 gtk_widget_show ( item );
4002 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4004 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4005 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4007 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4008 gtk_widget_show ( item );
4010 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4011 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4012 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4013 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4014 gtk_widget_show ( item );
4016 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4019 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4020 gtk_widget_show ( item );
4022 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4023 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4025 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4026 gtk_widget_show ( item );
4028 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4031 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4032 gtk_widget_show ( item );
4034 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4035 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4037 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4038 gtk_widget_show ( item );
4040 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4041 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4043 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4044 gtk_widget_show ( item );
4047 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4048 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4050 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4051 gtk_widget_show ( item );
4054 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4055 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4057 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4058 gtk_widget_show ( item );
4059 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4061 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4062 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4064 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4065 gtk_widget_show ( item );
4066 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4069 // Fake Waypoint UUIDs vi simple increasing integer
4070 static guint wp_uuid = 0;
4072 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4076 vik_waypoint_set_name (wp, name);
4078 if ( VIK_LAYER(vtl)->realized )
4080 // Do we need to create the sublayer:
4081 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4082 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4085 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4087 // Visibility column always needed for waypoints
4088 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 );
4090 // Actual setting of visibility dependent on the waypoint
4091 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4093 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4095 // Sort now as post_read is not called on a realized waypoint
4096 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4099 highest_wp_number_add_wp(vtl, name);
4100 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4104 // Fake Track UUIDs vi simple increasing integer
4105 static guint tr_uuid = 0;
4107 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4111 vik_track_set_name (t, name);
4113 if ( VIK_LAYER(vtl)->realized )
4115 // Do we need to create the sublayer:
4116 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4117 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4120 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4121 // Visibility column always needed for tracks
4122 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 );
4124 // Actual setting of visibility dependent on the track
4125 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4127 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4129 // Sort now as post_read is not called on a realized track
4130 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4133 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4135 trw_layer_update_treeview ( vtl, t );
4138 // Fake Route UUIDs vi simple increasing integer
4139 static guint rt_uuid = 0;
4141 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4145 vik_track_set_name (t, name);
4147 if ( VIK_LAYER(vtl)->realized )
4149 // Do we need to create the sublayer:
4150 if ( g_hash_table_size (vtl->routes) == 0 ) {
4151 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4154 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4155 // Visibility column always needed for routes
4156 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 );
4157 // Actual setting of visibility dependent on the route
4158 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4160 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4162 // Sort now as post_read is not called on a realized route
4163 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4166 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4168 trw_layer_update_treeview ( vtl, t );
4171 /* to be called whenever a track has been deleted or may have been changed. */
4172 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4174 if (vtl->current_tp_track == trk )
4175 trw_layer_cancel_current_tp ( vtl, FALSE );
4179 * Normally this is done to due the waypoint size preference having changed
4181 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4183 GHashTableIter iter;
4184 gpointer key, value;
4187 g_hash_table_iter_init ( &iter, vtl->waypoints );
4188 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4189 VikWaypoint *wp = VIK_WAYPOINT(value);
4191 // Reapply symbol setting to update the pixbuf
4192 gchar *tmp_symbol = g_strdup ( wp->symbol );
4193 vik_waypoint_set_symbol ( wp, tmp_symbol );
4194 g_free ( tmp_symbol );
4200 * trw_layer_new_unique_sublayer_name:
4202 * Allocates a unique new name
4204 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4207 gchar *newname = g_strdup(name);
4212 switch ( sublayer_type ) {
4213 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4214 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4216 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4217 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4220 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4223 // If found a name already in use try adding 1 to it and we try again
4225 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4227 newname = new_newname;
4230 } while ( id != NULL);
4235 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4237 // No more uniqueness of name forced when loading from a file
4238 // This now makes this function a little redunant as we just flow the parameters through
4239 vik_trw_layer_add_waypoint ( vtl, name, wp );
4242 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4244 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4245 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4246 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4247 vik_track_free ( tr );
4248 vtl->route_finder_append = FALSE; /* this means we have added it */
4251 // No more uniqueness of name forced when loading from a file
4253 vik_trw_layer_add_route ( vtl, name, tr );
4255 vik_trw_layer_add_track ( vtl, name, tr );
4257 if ( vtl->route_finder_check_added_track ) {
4258 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4259 vtl->route_finder_added_track = tr;
4264 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4266 *l = g_list_append(*l, id);
4270 * Move an item from one TRW layer to another TRW layer
4272 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4274 // TODO reconsider strategy when moving within layer (if anything...)
4275 gboolean rename = ( vtl_src != vtl_dest );
4279 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4280 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4284 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4286 newname = g_strdup ( trk->name );
4288 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4289 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4291 vik_trw_layer_delete_track ( vtl_src, trk );
4294 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4295 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4299 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4301 newname = g_strdup ( trk->name );
4303 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4304 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4306 vik_trw_layer_delete_route ( vtl_src, trk );
4309 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4310 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4314 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4316 newname = g_strdup ( wp->name );
4318 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4319 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4321 trw_layer_delete_waypoint ( vtl_src, wp );
4323 // Recalculate bounds even if not renamed as maybe dragged between layers
4324 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4325 trw_layer_calculate_bounds_waypoints ( vtl_src );
4329 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4331 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4332 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4334 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4335 GList *items = NULL;
4338 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4339 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4341 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4342 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4344 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4345 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4350 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4351 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4352 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4353 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4355 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4362 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4363 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4367 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4369 trku_udata *user_data = udata;
4370 if ( trk == user_data->trk ) {
4371 user_data->uuid = id;
4377 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4379 gboolean was_visible = FALSE;
4381 if ( trk && trk->name ) {
4383 if ( trk == vtl->current_track ) {
4384 vtl->current_track = NULL;
4385 vtl->current_tp_track = NULL;
4386 vtl->current_tp_id = NULL;
4387 vtl->moving_tp = FALSE;
4390 was_visible = trk->visible;
4392 if ( trk == vtl->route_finder_current_track )
4393 vtl->route_finder_current_track = NULL;
4395 if ( trk == vtl->route_finder_added_track )
4396 vtl->route_finder_added_track = NULL;
4402 // Hmmm, want key of it
4403 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4405 if ( trkf && udata.uuid ) {
4406 /* could be current_tp, so we have to check */
4407 trw_layer_cancel_tps_of_track ( vtl, trk );
4409 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4412 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4413 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4414 g_hash_table_remove ( vtl->tracks, udata.uuid );
4416 // If last sublayer, then remove sublayer container
4417 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4418 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4426 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4428 gboolean was_visible = FALSE;
4430 if ( trk && trk->name ) {
4432 if ( trk == vtl->current_track ) {
4433 vtl->current_track = NULL;
4434 vtl->current_tp_track = NULL;
4435 vtl->current_tp_id = NULL;
4436 vtl->moving_tp = FALSE;
4439 was_visible = trk->visible;
4441 if ( trk == vtl->route_finder_current_track )
4442 vtl->route_finder_current_track = NULL;
4444 if ( trk == vtl->route_finder_added_track )
4445 vtl->route_finder_added_track = NULL;
4451 // Hmmm, want key of it
4452 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4454 if ( trkf && udata.uuid ) {
4455 /* could be current_tp, so we have to check */
4456 trw_layer_cancel_tps_of_track ( vtl, trk );
4458 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4461 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4462 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4463 g_hash_table_remove ( vtl->routes, udata.uuid );
4465 // If last sublayer, then remove sublayer container
4466 if ( g_hash_table_size (vtl->routes) == 0 ) {
4467 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4475 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4477 gboolean was_visible = FALSE;
4479 if ( wp && wp->name ) {
4481 if ( wp == vtl->current_wp ) {
4482 vtl->current_wp = NULL;
4483 vtl->current_wp_id = NULL;
4484 vtl->moving_wp = FALSE;
4487 was_visible = wp->visible;
4493 // Hmmm, want key of it
4494 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4496 if ( wpf && udata.uuid ) {
4497 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4500 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4501 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4503 highest_wp_number_remove_wp(vtl, wp->name);
4504 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4506 // If last sublayer, then remove sublayer container
4507 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4508 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4518 // Only for temporary use by trw_layer_delete_waypoint_by_name
4519 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4521 wpu_udata *user_data = udata;
4522 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4523 user_data->uuid = id;
4530 * Delete a waypoint by the given name
4531 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4532 * as there be multiple waypoints with the same name
4534 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4537 // Fake a waypoint with the given name
4538 udata.wp = vik_waypoint_new ();
4539 vik_waypoint_set_name (udata.wp, name);
4540 // Currently only the name is used in this waypoint find function
4543 // Hmmm, want key of it
4544 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4546 vik_waypoint_free (udata.wp);
4548 if ( wpf && udata.uuid )
4549 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4555 VikTrack *trk; // input
4556 gpointer uuid; // output
4559 // Only for temporary use by trw_layer_delete_track_by_name
4560 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4562 tpu_udata *user_data = udata;
4563 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4564 user_data->uuid = id;
4571 * Delete a track by the given name
4572 * NOTE: ATM this will delete the first encountered Track with the specified name
4573 * as there may be multiple tracks with the same name within the specified hash table
4575 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4578 // Fake a track with the given name
4579 udata.trk = vik_track_new ();
4580 vik_track_set_name (udata.trk, name);
4581 // Currently only the name is used in this waypoint find function
4584 // Hmmm, want key of it
4585 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4587 vik_track_free (udata.trk);
4589 if ( trkf && udata.uuid ) {
4590 // This could be a little better written...
4591 if ( vtl->tracks == ht_tracks )
4592 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4593 if ( vtl->routes == ht_tracks )
4594 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4601 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4603 vik_treeview_item_delete (vt, it );
4606 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4609 vtl->current_track = NULL;
4610 vtl->route_finder_current_track = NULL;
4611 vtl->route_finder_added_track = NULL;
4612 if (vtl->current_tp_track)
4613 trw_layer_cancel_current_tp(vtl, FALSE);
4615 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4616 g_hash_table_remove_all(vtl->routes_iters);
4617 g_hash_table_remove_all(vtl->routes);
4619 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4621 vik_layer_emit_update ( VIK_LAYER(vtl) );
4624 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4627 vtl->current_track = NULL;
4628 vtl->route_finder_current_track = NULL;
4629 vtl->route_finder_added_track = NULL;
4630 if (vtl->current_tp_track)
4631 trw_layer_cancel_current_tp(vtl, FALSE);
4633 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4634 g_hash_table_remove_all(vtl->tracks_iters);
4635 g_hash_table_remove_all(vtl->tracks);
4637 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4639 vik_layer_emit_update ( VIK_LAYER(vtl) );
4642 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4644 vtl->current_wp = NULL;
4645 vtl->current_wp_id = NULL;
4646 vtl->moving_wp = FALSE;
4648 highest_wp_number_reset(vtl);
4650 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4651 g_hash_table_remove_all(vtl->waypoints_iters);
4652 g_hash_table_remove_all(vtl->waypoints);
4654 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4656 vik_layer_emit_update ( VIK_LAYER(vtl) );
4659 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4661 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4662 // Get confirmation from the user
4663 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4664 _("Are you sure you want to delete all tracks in %s?"),
4665 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4666 vik_trw_layer_delete_all_tracks (vtl);
4669 static void trw_layer_delete_all_routes ( 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 routes in %s?"),
4675 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4676 vik_trw_layer_delete_all_routes (vtl);
4679 static void trw_layer_delete_all_waypoints ( 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 waypoints in %s?"),
4685 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4686 vik_trw_layer_delete_all_waypoints (vtl);
4689 static void trw_layer_delete_item ( gpointer pass_along[6] )
4691 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4692 gboolean was_visible = FALSE;
4693 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4695 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4696 if ( wp && wp->name ) {
4697 if ( GPOINTER_TO_INT ( pass_along[4]) )
4698 // Get confirmation from the user
4699 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4700 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4701 _("Are you sure you want to delete the waypoint \"%s\"?"),
4704 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4707 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4709 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4710 if ( trk && trk->name ) {
4711 if ( GPOINTER_TO_INT ( pass_along[4]) )
4712 // Get confirmation from the user
4713 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4714 _("Are you sure you want to delete the track \"%s\"?"),
4717 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4722 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4723 if ( trk && trk->name ) {
4724 if ( GPOINTER_TO_INT ( pass_along[4]) )
4725 // Get confirmation from the user
4726 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4727 _("Are you sure you want to delete the route \"%s\"?"),
4730 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4734 vik_layer_emit_update ( VIK_LAYER(vtl) );
4738 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4740 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4742 vik_waypoint_set_name ( wp, new_name );
4744 // Now update the treeview as well
4749 // Need key of it for treeview update
4750 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4752 if ( wpf && udataU.uuid ) {
4753 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4756 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4757 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4763 * Maintain icon of waypoint in the treeview
4765 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4767 // update the treeview
4772 // Need key of it for treeview update
4773 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4775 if ( wpf && udataU.uuid ) {
4776 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4779 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4784 static void trw_layer_properties_item ( gpointer pass_along[7] )
4786 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4787 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4789 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4791 if ( wp && wp->name )
4793 gboolean updated = FALSE;
4794 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4796 trw_layer_waypoint_rename ( vtl, wp, new_name );
4798 if ( updated && pass_along[6] )
4799 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4801 if ( updated && VIK_LAYER(vtl)->visible )
4802 vik_layer_emit_update ( VIK_LAYER(vtl) );
4808 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4809 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4811 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4813 if ( tr && tr->name )
4815 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4818 pass_along[1], /* vlp */
4819 pass_along[5], /* vvp */
4826 * trw_layer_track_statistics:
4828 * Show track statistics.
4829 * ATM jump to the stats page in the properties
4830 * TODO: consider separating the stats into an individual dialog?
4832 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4834 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4836 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4837 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4839 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4841 if ( trk && trk->name ) {
4842 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4845 pass_along[1], // vlp
4846 pass_along[5], // vvp
4852 * Update the treeview of the track id - primarily to update the icon
4854 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4860 gpointer *trkf = NULL;
4861 if ( trk->is_route )
4862 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4864 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4866 if ( trkf && udata.uuid ) {
4868 GtkTreeIter *iter = NULL;
4869 if ( trk->is_route )
4870 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4872 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4875 // TODO: Make this a function
4876 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4877 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4878 ((trk->color.green & 0xff00) << 8) |
4879 (trk->color.blue & 0xff00);
4880 gdk_pixbuf_fill ( pixbuf, pixel );
4881 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4882 g_object_unref (pixbuf);
4889 Parameter 1 -> VikLayersPanel
4890 Parameter 2 -> VikLayer
4891 Parameter 3 -> VikViewport
4893 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4896 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4897 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4900 /* since vlp not set, vl & vvp should be valid instead! */
4902 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4903 vik_layer_emit_update ( VIK_LAYER(vl) );
4908 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4910 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4912 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4913 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4915 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4917 if ( track && track->trackpoints )
4918 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4921 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4923 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4925 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4926 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4928 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4930 if ( track && track->trackpoints )
4932 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4934 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4935 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4936 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4937 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4938 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4942 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4944 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4946 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4947 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4949 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4954 // Converting a track to a route can be a bit more complicated,
4955 // so give a chance to change our minds:
4956 if ( !trk->is_route &&
4957 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4958 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4960 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4961 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4966 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4969 trk_copy->is_route = !trk_copy->is_route;
4971 // ATM can't set name to self - so must create temporary copy
4972 gchar *name = g_strdup ( trk_copy->name );
4974 // Delete old one and then add new one
4975 if ( trk->is_route ) {
4976 vik_trw_layer_delete_route ( vtl, trk );
4977 vik_trw_layer_add_track ( vtl, name, trk_copy );
4980 // Extra route conversion bits...
4981 vik_track_merge_segments ( trk_copy );
4982 vik_track_to_routepoints ( trk_copy );
4984 vik_trw_layer_delete_track ( vtl, trk );
4985 vik_trw_layer_add_route ( vtl, name, trk_copy );
4989 // Update in case color of track / route changes when moving between sublayers
4990 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4993 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4995 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4997 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4998 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5000 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5003 vik_track_anonymize_times ( track );
5006 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
5008 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5010 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5011 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5013 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5018 vtl->current_track = track;
5019 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);
5021 if ( track->trackpoints )
5022 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
5026 * extend a track using route finder
5028 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
5030 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5031 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
5034 if ( !track->trackpoints )
5036 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
5038 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5039 vtl->route_finder_coord = last_coord;
5040 vtl->route_finder_current_track = track;
5041 vtl->route_finder_started = TRUE;
5043 if ( track->trackpoints )
5044 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
5051 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5053 // If have a vlp then perform a basic test to see if any DEM info available...
5055 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5057 if ( !g_list_length(dems) ) {
5058 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5066 * apply_dem_data_common:
5068 * A common function for applying the DEM values and reporting the results.
5070 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5072 if ( !trw_layer_dem_test ( vtl, vlp ) )
5075 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5076 // Inform user how much was changed
5078 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5079 g_snprintf(str, 64, tmp_str, changed);
5080 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5083 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
5085 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5087 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5088 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5090 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5093 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
5096 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
5098 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5100 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5101 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5103 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5106 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
5112 * A common function for applying the elevation smoothing and reporting the results.
5114 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5116 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5117 // Inform user how much was changed
5119 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5120 g_snprintf(str, 64, tmp_str, changed);
5121 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5127 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5129 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5131 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5132 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5134 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5139 smooth_it ( vtl, track, FALSE );
5142 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5144 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5146 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5147 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5149 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5154 smooth_it ( vtl, track, TRUE );
5158 * Commonal helper function
5160 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5163 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5164 g_snprintf(str, 64, tmp_str, changed);
5165 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5168 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5170 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5171 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5173 if ( !trw_layer_dem_test ( vtl, vlp ) )
5177 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5179 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5181 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5185 GHashTableIter iter;
5186 gpointer key, value;
5188 g_hash_table_iter_init ( &iter, vtl->waypoints );
5189 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5190 VikWaypoint *wp = VIK_WAYPOINT(value);
5191 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5194 wp_changed_message ( vtl, changed );
5197 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5199 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5200 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5202 if ( !trw_layer_dem_test ( vtl, vlp ) )
5206 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5208 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5210 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5214 GHashTableIter iter;
5215 gpointer key, value;
5217 g_hash_table_iter_init ( &iter, vtl->waypoints );
5218 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5219 VikWaypoint *wp = VIK_WAYPOINT(value);
5220 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5223 wp_changed_message ( vtl, changed );
5226 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5228 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5230 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5231 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5233 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5238 GList *trps = track->trackpoints;
5241 trps = g_list_last(trps);
5242 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5245 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5247 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5249 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5250 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5252 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5257 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5260 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5263 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5265 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5267 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5268 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5270 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5275 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5278 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5281 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5283 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5285 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5286 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5288 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5293 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5296 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5300 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5302 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5304 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5306 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5307 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5309 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5311 if ( trk && trk->trackpoints )
5313 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5314 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5315 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5316 if ( pass_along[1] )
5317 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5319 vik_layer_emit_update ( VIK_LAYER(vtl) );
5324 * Refine the selected track/route with a routing engine.
5325 * The routing engine is selected by the user, when requestiong the job.
5327 static void trw_layer_route_refine ( gpointer pass_along[6] )
5329 static gint last_engine = 0;
5330 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5333 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5334 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5336 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5338 if ( trk && trk->trackpoints )
5340 /* Check size of the route */
5341 int nb = vik_track_get_tp_count(trk);
5343 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5344 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5345 GTK_MESSAGE_WARNING,
5346 GTK_BUTTONS_OK_CANCEL,
5347 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5349 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5350 gtk_widget_destroy ( dialog );
5351 if (response != GTK_RESPONSE_OK )
5354 /* Select engine from dialog */
5355 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5356 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5357 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5359 GTK_RESPONSE_REJECT,
5361 GTK_RESPONSE_ACCEPT,
5363 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5364 gtk_widget_show_all(label);
5366 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5368 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5369 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5370 gtk_widget_show_all(combo);
5372 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5374 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5376 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5378 /* Dialog validated: retrieve selected engine and do the job */
5379 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5380 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5383 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5385 /* Force saving track */
5386 /* FIXME: remove or rename this hack */
5387 vtl->route_finder_check_added_track = TRUE;
5390 vik_routing_engine_refine (routing, vtl, trk);
5392 /* FIXME: remove or rename this hack */
5393 if ( vtl->route_finder_added_track )
5394 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5396 vtl->route_finder_added_track = NULL;
5397 vtl->route_finder_check_added_track = FALSE;
5399 vik_layer_emit_update ( VIK_LAYER(vtl) );
5401 /* Restore cursor */
5402 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5404 gtk_widget_destroy ( dialog );
5408 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5410 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5411 trw_layer_tpwin_init ( vtl );
5414 /*************************************
5415 * merge/split by time routines
5416 *************************************/
5418 /* called for each key in track hash table.
5419 * If the current track has the same time stamp type, add it to the result,
5420 * except the one pointed by "exclude".
5421 * set exclude to NULL if there is no exclude to check.
5422 * Note that the result is in reverse (for performance reasons).
5427 gboolean with_timestamps;
5429 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5431 twt_udata *user_data = udata;
5432 VikTrackpoint *p1, *p2;
5434 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5438 if (VIK_TRACK(value)->trackpoints) {
5439 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5440 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5442 if ( user_data->with_timestamps ) {
5443 if (!p1->has_timestamp || !p2->has_timestamp) {
5448 // Don't add tracks with timestamps when getting non timestamp tracks
5449 if (p1->has_timestamp || p2->has_timestamp) {
5455 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5458 /* called for each key in track hash table. if original track user_data[1] is close enough
5459 * to the passed one, add it to list in user_data[0]
5461 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5464 VikTrackpoint *p1, *p2;
5465 VikTrack *trk = VIK_TRACK(value);
5467 GList **nearby_tracks = ((gpointer *)user_data)[0];
5468 GList *tpoints = ((gpointer *)user_data)[1];
5471 * detect reasons for not merging, and return
5472 * if no reason is found not to merge, then do it.
5475 // Exclude the original track from the compiled list
5476 if (trk->trackpoints == tpoints) {
5480 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5481 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5483 if (trk->trackpoints) {
5484 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5485 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5487 if (!p1->has_timestamp || !p2->has_timestamp) {
5488 //g_print("no timestamp\n");
5492 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5493 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5494 if (! (abs(t1 - p2->timestamp) < threshold ||
5496 abs(p1->timestamp - t2) < threshold)
5503 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5506 /* comparison function used to sort tracks; a and b are hash table keys */
5507 /* Not actively used - can be restored if needed
5508 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5510 GHashTable *tracks = user_data;
5513 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5514 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5516 if (t1 < t2) return -1;
5517 if (t1 > t2) return 1;
5522 /* comparison function used to sort trackpoints */
5523 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5525 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5527 if (t1 < t2) return -1;
5528 if (t1 > t2) return 1;
5533 * comparison function which can be used to sort tracks or waypoints by name
5535 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5537 const gchar* namea = (const gchar*) a;
5538 const gchar* nameb = (const gchar*) b;
5539 if ( namea == NULL || nameb == NULL)
5542 // Same sort method as used in the vik_treeview_*_alphabetize functions
5543 return strcmp ( namea, nameb );
5547 * Attempt to merge selected track with other tracks specified by the user
5548 * Tracks to merge with must be of the same 'type' as the selected track -
5549 * either all with timestamps, or all without timestamps
5551 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5553 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5554 GList *other_tracks = NULL;
5555 GHashTable *ght_tracks;
5556 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5557 ght_tracks = vtl->routes;
5559 ght_tracks = vtl->tracks;
5561 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5566 if ( !track->trackpoints )
5570 udata.result = &other_tracks;
5571 udata.exclude = track->trackpoints;
5572 // Allow merging with 'similar' time type time tracks
5573 // i.e. either those times, or those without
5574 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5576 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5577 other_tracks = g_list_reverse(other_tracks);
5579 if ( !other_tracks ) {
5580 if ( udata.with_timestamps )
5581 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5583 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5587 // Sort alphabetically for user presentation
5588 // Convert into list of names for usage with dialog function
5589 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5590 GList *other_tracks_names = NULL;
5591 GList *iter = g_list_first ( other_tracks );
5593 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5594 iter = g_list_next ( iter );
5597 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5599 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5603 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5604 g_list_free(other_tracks);
5605 g_list_free(other_tracks_names);
5610 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5611 VikTrack *merge_track;
5612 if ( track->is_route )
5613 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5615 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5618 vik_track_steal_and_append_trackpoints ( track, merge_track );
5619 if ( track->is_route )
5620 vik_trw_layer_delete_route (vtl, merge_track);
5622 vik_trw_layer_delete_track (vtl, merge_track);
5623 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5626 for (l = merge_list; l != NULL; l = g_list_next(l))
5628 g_list_free(merge_list);
5630 vik_layer_emit_update( VIK_LAYER(vtl) );
5634 // c.f. trw_layer_sorted_track_id_by_name_list
5635 // but don't add the specified track to the list (normally current track)
5636 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5638 twt_udata *user_data = udata;
5641 if (trk->trackpoints == user_data->exclude) {
5645 // Sort named list alphabetically
5646 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5650 * Join - this allows combining 'tracks' and 'track routes'
5651 * i.e. doesn't care about whether tracks have consistent timestamps
5652 * ATM can only append one track at a time to the currently selected track
5654 static void trw_layer_append_track ( gpointer pass_along[6] )
5657 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5659 GHashTable *ght_tracks;
5660 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5661 ght_tracks = vtl->routes;
5663 ght_tracks = vtl->tracks;
5665 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5670 GList *other_tracks_names = NULL;
5672 // Sort alphabetically for user presentation
5673 // Convert into list of names for usage with dialog function
5674 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5676 udata.result = &other_tracks_names;
5677 udata.exclude = trk->trackpoints;
5679 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5681 // Note the limit to selecting one track only
5682 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5683 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5684 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5687 trk->is_route ? _("Append Route"): _("Append Track"),
5688 trk->is_route ? _("Select the route to append after the current route") :
5689 _("Select the track to append after the current track") );
5691 g_list_free(other_tracks_names);
5693 // It's a list, but shouldn't contain more than one other track!
5694 if ( append_list ) {
5696 for (l = append_list; l != NULL; l = g_list_next(l)) {
5697 // TODO: at present this uses the first track found by name,
5698 // which with potential multiple same named tracks may not be the one selected...
5699 VikTrack *append_track;
5700 if ( trk->is_route )
5701 append_track = vik_trw_layer_get_route ( vtl, l->data );
5703 append_track = vik_trw_layer_get_track ( vtl, l->data );
5705 if ( append_track ) {
5706 vik_track_steal_and_append_trackpoints ( trk, append_track );
5707 if ( trk->is_route )
5708 vik_trw_layer_delete_route (vtl, append_track);
5710 vik_trw_layer_delete_track (vtl, append_track);
5713 for (l = append_list; l != NULL; l = g_list_next(l))
5715 g_list_free(append_list);
5717 vik_layer_emit_update( VIK_LAYER(vtl) );
5722 * Very similar to trw_layer_append_track for joining
5723 * but this allows selection from the 'other' list
5724 * If a track is selected, then is shows routes and joins the selected one
5725 * If a route is selected, then is shows tracks and joins the selected one
5727 static void trw_layer_append_other ( gpointer pass_along[6] )
5730 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5732 GHashTable *ght_mykind, *ght_others;
5733 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5734 ght_mykind = vtl->routes;
5735 ght_others = vtl->tracks;
5738 ght_mykind = vtl->tracks;
5739 ght_others = vtl->routes;
5742 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5747 GList *other_tracks_names = NULL;
5749 // Sort alphabetically for user presentation
5750 // Convert into list of names for usage with dialog function
5751 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5753 udata.result = &other_tracks_names;
5754 udata.exclude = trk->trackpoints;
5756 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5758 // Note the limit to selecting one track only
5759 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5760 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5761 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5764 trk->is_route ? _("Append Track"): _("Append Route"),
5765 trk->is_route ? _("Select the track to append after the current route") :
5766 _("Select the route to append after the current track") );
5768 g_list_free(other_tracks_names);
5770 // It's a list, but shouldn't contain more than one other track!
5771 if ( append_list ) {
5773 for (l = append_list; l != NULL; l = g_list_next(l)) {
5774 // TODO: at present this uses the first track found by name,
5775 // which with potential multiple same named tracks may not be the one selected...
5777 // Get FROM THE OTHER TYPE list
5778 VikTrack *append_track;
5779 if ( trk->is_route )
5780 append_track = vik_trw_layer_get_track ( vtl, l->data );
5782 append_track = vik_trw_layer_get_route ( vtl, l->data );
5784 if ( append_track ) {
5786 if ( !append_track->is_route &&
5787 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5788 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5790 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5791 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5792 vik_track_merge_segments ( append_track );
5793 vik_track_to_routepoints ( append_track );
5800 vik_track_steal_and_append_trackpoints ( trk, append_track );
5802 // Delete copied which is FROM THE OTHER TYPE list
5803 if ( trk->is_route )
5804 vik_trw_layer_delete_track (vtl, append_track);
5806 vik_trw_layer_delete_route (vtl, append_track);
5809 for (l = append_list; l != NULL; l = g_list_next(l))
5811 g_list_free(append_list);
5812 vik_layer_emit_update( VIK_LAYER(vtl) );
5816 /* merge by segments */
5817 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5819 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5820 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5821 guint segments = vik_track_merge_segments ( trk );
5822 // NB currently no need to redraw as segments not actually shown on the display
5823 // However inform the user of what happened:
5825 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5826 g_snprintf(str, 64, tmp_str, segments);
5827 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5830 /* merge by time routine */
5831 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5833 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5837 GList *tracks_with_timestamp = NULL;
5838 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5839 if (orig_trk->trackpoints &&
5840 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5841 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5846 udata.result = &tracks_with_timestamp;
5847 udata.exclude = orig_trk->trackpoints;
5848 udata.with_timestamps = TRUE;
5849 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5850 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5852 if (!tracks_with_timestamp) {
5853 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5856 g_list_free(tracks_with_timestamp);
5858 static guint threshold_in_minutes = 1;
5859 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5860 _("Merge Threshold..."),
5861 _("Merge when time between tracks less than:"),
5862 &threshold_in_minutes)) {
5866 // keep attempting to merge all tracks until no merges within the time specified is possible
5867 gboolean attempt_merge = TRUE;
5868 GList *nearby_tracks = NULL;
5870 static gpointer params[3];
5872 while ( attempt_merge ) {
5874 // Don't try again unless tracks have changed
5875 attempt_merge = FALSE;
5877 trps = orig_trk->trackpoints;
5881 if (nearby_tracks) {
5882 g_list_free(nearby_tracks);
5883 nearby_tracks = NULL;
5886 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5887 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5889 /* g_print("Original track times: %d and %d\n", t1, t2); */
5890 params[0] = &nearby_tracks;
5891 params[1] = (gpointer)trps;
5892 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5894 /* get a list of adjacent-in-time tracks */
5895 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5898 GList *l = nearby_tracks;
5901 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5902 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5904 t1 = get_first_trackpoint(l)->timestamp;
5905 t2 = get_last_trackpoint(l)->timestamp;
5906 #undef get_first_trackpoint
5907 #undef get_last_trackpoint
5908 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5911 /* remove trackpoints from merged track, delete track */
5912 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5913 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5915 // Tracks have changed, therefore retry again against all the remaining tracks
5916 attempt_merge = TRUE;
5921 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5924 g_list_free(nearby_tracks);
5926 vik_layer_emit_update( VIK_LAYER(vtl) );
5930 * Split a track at the currently selected trackpoint
5932 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5934 if ( !vtl->current_tpl )
5937 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5938 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5940 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5941 GList *newglist = g_list_alloc ();
5942 newglist->prev = NULL;
5943 newglist->next = vtl->current_tpl->next;
5944 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5945 tr->trackpoints = newglist;
5947 vtl->current_tpl->next->prev = newglist; /* end old track here */
5948 vtl->current_tpl->next = NULL;
5950 // Bounds of the selected track changed due to the split
5951 vik_track_calculate_bounds ( vtl->current_tp_track );
5953 vtl->current_tpl = newglist; /* change tp to first of new track. */
5954 vtl->current_tp_track = tr;
5957 vik_trw_layer_add_route ( vtl, name, tr );
5959 vik_trw_layer_add_track ( vtl, name, tr );
5961 // Bounds of the new track created by the split
5962 vik_track_calculate_bounds ( tr );
5968 // Also need id of newly created track
5971 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5973 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5975 if ( trkf && udata.uuid )
5976 vtl->current_tp_id = udata.uuid;
5978 vtl->current_tp_id = NULL;
5980 vik_layer_emit_update(VIK_LAYER(vtl));
5986 /* split by time routine */
5987 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5989 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5990 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5991 GList *trps = track->trackpoints;
5993 GList *newlists = NULL;
5994 GList *newtps = NULL;
5995 static guint thr = 1;
6002 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6003 _("Split Threshold..."),
6004 _("Split when time between trackpoints exceeds:"),
6009 /* iterate through trackpoints, and copy them into new lists without touching original list */
6010 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6014 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6016 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6019 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6020 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6021 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6023 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
6028 if (ts - prev_ts > thr*60) {
6029 /* flush accumulated trackpoints into new list */
6030 newlists = g_list_append(newlists, g_list_reverse(newtps));
6034 /* accumulate trackpoint copies in newtps, in reverse order */
6035 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6037 iter = g_list_next(iter);
6040 newlists = g_list_append(newlists, g_list_reverse(newtps));
6043 /* put lists of trackpoints into tracks */
6045 // Only bother updating if the split results in new tracks
6046 if (g_list_length (newlists) > 1) {
6051 tr = vik_track_copy ( track, FALSE );
6052 tr->trackpoints = (GList *)(iter->data);
6054 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6055 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6056 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
6057 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
6058 g_free ( new_tr_name );
6059 vik_track_calculate_bounds ( tr );
6060 iter = g_list_next(iter);
6062 // Remove original track and then update the display
6063 vik_trw_layer_delete_track (vtl, track);
6064 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6066 g_list_free(newlists);
6070 * Split a track by the number of points as specified by the user
6072 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
6074 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6076 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6077 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6079 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6084 // Check valid track
6085 GList *trps = track->trackpoints;
6089 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6090 _("Split Every Nth Point"),
6091 _("Split on every Nth point:"),
6092 250, // Default value as per typical limited track capacity of various GPS devices
6096 // Was a valid number returned?
6102 GList *newlists = NULL;
6103 GList *newtps = NULL;
6108 /* accumulate trackpoint copies in newtps, in reverse order */
6109 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6111 if (count >= points) {
6112 /* flush accumulated trackpoints into new list */
6113 newlists = g_list_append(newlists, g_list_reverse(newtps));
6117 iter = g_list_next(iter);
6120 // If there is a remaining chunk put that into the new split list
6121 // This may well be the whole track if no split points were encountered
6123 newlists = g_list_append(newlists, g_list_reverse(newtps));
6126 /* put lists of trackpoints into tracks */
6128 // Only bother updating if the split results in new tracks
6129 if (g_list_length (newlists) > 1) {
6134 tr = vik_track_copy ( track, FALSE );
6135 tr->trackpoints = (GList *)(iter->data);
6137 if ( track->is_route ) {
6138 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6139 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6142 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6143 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6145 g_free ( new_tr_name );
6146 vik_track_calculate_bounds ( tr );
6148 iter = g_list_next(iter);
6150 // Remove original track and then update the display
6151 if ( track->is_route )
6152 vik_trw_layer_delete_route (vtl, track);
6154 vik_trw_layer_delete_track (vtl, track);
6155 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6157 g_list_free(newlists);
6161 * Split a track at the currently selected trackpoint
6163 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6165 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6166 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6167 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6171 * Split a track by its segments
6172 * Routes do not have segments so don't call this for routes
6174 static void trw_layer_split_segments ( gpointer pass_along[6] )
6176 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6177 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6184 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6187 for ( i = 0; i < ntracks; i++ ) {
6189 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6190 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6191 g_free ( new_tr_name );
6196 // Remove original track
6197 vik_trw_layer_delete_track ( vtl, trk );
6198 vik_layer_emit_update ( VIK_LAYER(vtl) );
6201 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6204 /* end of split/merge routines */
6206 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6210 // Find available adjacent trackpoint
6211 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6212 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6213 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6215 // Delete current trackpoint
6216 vik_trackpoint_free ( vtl->current_tpl->data );
6217 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6219 // Set to current to the available adjacent trackpoint
6220 vtl->current_tpl = new_tpl;
6222 if ( vtl->current_tp_track ) {
6223 vik_track_calculate_bounds ( vtl->current_tp_track );
6227 // Delete current trackpoint
6228 vik_trackpoint_free ( vtl->current_tpl->data );
6229 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6230 trw_layer_cancel_current_tp ( vtl, FALSE );
6235 * Delete the selected point
6237 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6239 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6241 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6242 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6244 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6249 if ( !vtl->current_tpl )
6252 trw_layer_trackpoint_selected_delete ( vtl, trk );
6254 // Track has been updated so update tps:
6255 trw_layer_cancel_tps_of_track ( vtl, trk );
6257 vik_layer_emit_update ( VIK_LAYER(vtl) );
6261 * Delete adjacent track points at the same position
6262 * AKA Delete Dulplicates on the Properties Window
6264 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6266 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6268 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6269 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6271 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6276 gulong removed = vik_track_remove_dup_points ( trk );
6278 // Track has been updated so update tps:
6279 trw_layer_cancel_tps_of_track ( vtl, trk );
6281 // Inform user how much was deleted as it's not obvious from the normal view
6283 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6284 g_snprintf(str, 64, tmp_str, removed);
6285 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6287 vik_layer_emit_update ( VIK_LAYER(vtl) );
6291 * Delete adjacent track points with the same timestamp
6292 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6294 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6296 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6298 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6299 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6301 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6306 gulong removed = vik_track_remove_same_time_points ( trk );
6308 // Track has been updated so update tps:
6309 trw_layer_cancel_tps_of_track ( vtl, trk );
6311 // Inform user how much was deleted as it's not obvious from the normal view
6313 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6314 g_snprintf(str, 64, tmp_str, removed);
6315 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6317 vik_layer_emit_update ( VIK_LAYER(vtl) );
6323 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6325 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6327 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6328 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6330 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6335 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6337 vik_layer_emit_update ( VIK_LAYER(vtl) );
6340 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6342 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6344 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6345 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6347 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6352 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6354 vik_layer_emit_update ( VIK_LAYER(vtl) );
6360 static void trw_layer_reverse ( gpointer pass_along[6] )
6362 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6364 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6365 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6367 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6372 vik_track_reverse ( track );
6374 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6378 * Similar to trw_layer_enum_item, but this uses a sorted method
6381 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6383 GList **list = (GList**)udata;
6384 // *list = g_list_prepend(*all, key); //unsorted method
6385 // Sort named list alphabetically
6386 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6391 * Now Waypoint specific sort
6393 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6395 GList **list = (GList**)udata;
6396 // Sort named list alphabetically
6397 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6401 * Track specific sort
6403 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6405 GList **list = (GList**)udata;
6406 // Sort named list alphabetically
6407 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6412 gboolean has_same_track_name;
6413 const gchar *same_track_name;
6414 } same_track_name_udata;
6416 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6418 const gchar* namea = (const gchar*) aa;
6419 const gchar* nameb = (const gchar*) bb;
6422 gint result = strcmp ( namea, nameb );
6424 if ( result == 0 ) {
6425 // Found two names the same
6426 same_track_name_udata *user_data = udata;
6427 user_data->has_same_track_name = TRUE;
6428 user_data->same_track_name = namea;
6431 // Leave ordering the same
6436 * Find out if any tracks have the same name in this hash table
6438 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6440 // Sort items by name, then compare if any next to each other are the same
6442 GList *track_names = NULL;
6443 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6446 if ( ! track_names )
6449 same_track_name_udata udata;
6450 udata.has_same_track_name = FALSE;
6452 // Use sort routine to traverse list comparing items
6453 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6454 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6455 // Still no tracks...
6459 return udata.has_same_track_name;
6463 * Force unqiue track names for the track table specified
6464 * Note the panel is a required parameter to enable the update of the names displayed
6465 * Specify if on tracks or else on routes
6467 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6469 // . Search list for an instance of repeated name
6470 // . get track of this name
6471 // . create new name
6472 // . rename track & update equiv. treeview iter
6473 // . repeat until all different
6475 same_track_name_udata udata;
6477 GList *track_names = NULL;
6478 udata.has_same_track_name = FALSE;
6479 udata.same_track_name = NULL;
6481 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6484 if ( ! track_names )
6487 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6489 // Still no tracks...
6490 if ( ! dummy_list1 )
6493 while ( udata.has_same_track_name ) {
6495 // Find a track with the same name
6498 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6500 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6504 g_critical("Houston, we've had a problem.");
6505 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6506 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6511 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6512 vik_track_set_name ( trk, newname );
6518 // Need want key of it for treeview update
6519 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6521 if ( trkf && udataU.uuid ) {
6525 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6527 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6530 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6532 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6534 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6538 // Start trying to find same names again...
6540 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6541 udata.has_same_track_name = FALSE;
6542 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6544 // No tracks any more - give up searching
6545 if ( ! dummy_list2 )
6546 udata.has_same_track_name = FALSE;
6550 vik_layers_panel_emit_update ( vlp );
6553 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6555 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6558 switch (GPOINTER_TO_INT (pass_along[2])) {
6559 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6560 iter = &(vtl->tracks_iter);
6561 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6563 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6564 iter = &(vtl->routes_iter);
6565 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6567 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6568 iter = &(vtl->waypoints_iter);
6569 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6573 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6576 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6578 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6581 switch (GPOINTER_TO_INT (pass_along[2])) {
6582 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6583 iter = &(vtl->tracks_iter);
6584 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6586 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6587 iter = &(vtl->routes_iter);
6588 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6590 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6591 iter = &(vtl->waypoints_iter);
6592 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6596 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6602 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6604 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6607 // Ensure list of track names offered is unique
6608 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6609 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6610 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6611 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6617 // Sort list alphabetically for better presentation
6618 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6621 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6625 // Get list of items to delete from the user
6626 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6629 _("Delete Selection"),
6630 _("Select tracks to delete"));
6633 // Delete requested tracks
6634 // since specificly requested, IMHO no need for extra confirmation
6635 if ( delete_list ) {
6637 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6638 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6639 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6641 g_list_free(delete_list);
6642 vik_layer_emit_update( VIK_LAYER(vtl) );
6649 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6651 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6654 // Ensure list of track names offered is unique
6655 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6656 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6657 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6658 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6664 // Sort list alphabetically for better presentation
6665 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6668 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6672 // Get list of items to delete from the user
6673 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6676 _("Delete Selection"),
6677 _("Select routes to delete") );
6680 // Delete requested routes
6681 // since specificly requested, IMHO no need for extra confirmation
6682 if ( delete_list ) {
6684 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6685 // This deletes first route it finds of that name (but uniqueness is enforced above)
6686 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6688 g_list_free(delete_list);
6689 vik_layer_emit_update( VIK_LAYER(vtl) );
6694 gboolean has_same_waypoint_name;
6695 const gchar *same_waypoint_name;
6696 } same_waypoint_name_udata;
6698 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6700 const gchar* namea = (const gchar*) aa;
6701 const gchar* nameb = (const gchar*) bb;
6704 gint result = strcmp ( namea, nameb );
6706 if ( result == 0 ) {
6707 // Found two names the same
6708 same_waypoint_name_udata *user_data = udata;
6709 user_data->has_same_waypoint_name = TRUE;
6710 user_data->same_waypoint_name = namea;
6713 // Leave ordering the same
6718 * Find out if any waypoints have the same name in this layer
6720 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6722 // Sort items by name, then compare if any next to each other are the same
6724 GList *waypoint_names = NULL;
6725 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6728 if ( ! waypoint_names )
6731 same_waypoint_name_udata udata;
6732 udata.has_same_waypoint_name = FALSE;
6734 // Use sort routine to traverse list comparing items
6735 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6736 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6737 // Still no waypoints...
6741 return udata.has_same_waypoint_name;
6745 * Force unqiue waypoint names for this layer
6746 * Note the panel is a required parameter to enable the update of the names displayed
6748 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6750 // . Search list for an instance of repeated name
6751 // . get waypoint of this name
6752 // . create new name
6753 // . rename waypoint & update equiv. treeview iter
6754 // . repeat until all different
6756 same_waypoint_name_udata udata;
6758 GList *waypoint_names = NULL;
6759 udata.has_same_waypoint_name = FALSE;
6760 udata.same_waypoint_name = NULL;
6762 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6765 if ( ! waypoint_names )
6768 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6770 // Still no waypoints...
6771 if ( ! dummy_list1 )
6774 while ( udata.has_same_waypoint_name ) {
6776 // Find a waypoint with the same name
6777 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6781 g_critical("Houston, we've had a problem.");
6782 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6783 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6788 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6790 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6792 // Start trying to find same names again...
6793 waypoint_names = NULL;
6794 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6795 udata.has_same_waypoint_name = FALSE;
6796 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6798 // No waypoints any more - give up searching
6799 if ( ! dummy_list2 )
6800 udata.has_same_waypoint_name = FALSE;
6804 vik_layers_panel_emit_update ( vlp );
6810 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6812 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6815 // Ensure list of waypoint names offered is unique
6816 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6817 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6818 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6819 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6825 // Sort list alphabetically for better presentation
6826 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6828 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6832 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6834 // Get list of items to delete from the user
6835 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6838 _("Delete Selection"),
6839 _("Select waypoints to delete"));
6842 // Delete requested waypoints
6843 // since specificly requested, IMHO no need for extra confirmation
6844 if ( delete_list ) {
6846 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6847 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6848 trw_layer_delete_waypoint_by_name (vtl, l->data);
6850 g_list_free(delete_list);
6852 trw_layer_calculate_bounds_waypoints ( vtl );
6853 vik_layer_emit_update( VIK_LAYER(vtl) );
6861 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6863 vik_treeview_item_toggle_visible ( vt, it );
6869 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6871 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6877 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6879 wp->visible = GPOINTER_TO_INT (on_off);
6885 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6887 wp->visible = !wp->visible;
6893 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6895 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6896 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6897 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6898 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6900 vik_layer_emit_update ( VIK_LAYER(vtl) );
6906 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6908 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6909 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6910 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6911 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6913 vik_layer_emit_update ( VIK_LAYER(vtl) );
6919 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6921 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6922 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6923 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6925 vik_layer_emit_update ( VIK_LAYER(vtl) );
6931 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6933 trk->visible = GPOINTER_TO_INT (on_off);
6939 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6941 trk->visible = !trk->visible;
6947 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6949 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6950 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6951 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6952 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6954 vik_layer_emit_update ( VIK_LAYER(vtl) );
6960 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6962 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6963 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6964 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6965 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6967 vik_layer_emit_update ( VIK_LAYER(vtl) );
6973 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6975 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6976 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6977 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6979 vik_layer_emit_update ( VIK_LAYER(vtl) );
6985 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6987 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6988 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6989 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6990 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6992 vik_layer_emit_update ( VIK_LAYER(vtl) );
6998 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
7000 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7001 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7002 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7003 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7005 vik_layer_emit_update ( VIK_LAYER(vtl) );
7011 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
7013 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7014 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7015 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7017 vik_layer_emit_update ( VIK_LAYER(vtl) );
7021 * vik_trw_layer_build_waypoint_list_t:
7023 * Helper function to construct a list of #vik_trw_waypoint_list_t
7025 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7027 GList *waypoints_and_layers = NULL;
7028 // build waypoints_and_layers list
7029 while ( waypoints ) {
7030 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7031 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7033 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7034 waypoints = g_list_next ( waypoints );
7036 return waypoints_and_layers;
7040 * trw_layer_create_waypoint_list:
7042 * Create the latest list of waypoints with the associated layer(s)
7043 * Although this will always be from a single layer here
7045 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7047 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7048 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7050 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7054 * trw_layer_analyse_close:
7056 * Stuff to do on dialog closure
7058 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7060 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7061 gtk_widget_destroy ( dialog );
7062 vtl->tracks_analysis_dialog = NULL;
7066 * vik_trw_layer_build_track_list_t:
7068 * Helper function to construct a list of #vik_trw_track_list_t
7070 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7072 GList *tracks_and_layers = NULL;
7073 // build tracks_and_layers list
7075 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7076 vtdl->trk = VIK_TRACK(tracks->data);
7078 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7079 tracks = g_list_next ( tracks );
7081 return tracks_and_layers;
7085 * trw_layer_create_track_list:
7087 * Create the latest list of tracks with the associated layer(s)
7088 * Although this will always be from a single layer here
7090 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7092 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7093 GList *tracks = NULL;
7094 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7095 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7097 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7099 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7102 static void trw_layer_tracks_stats ( gpointer lav[2] )
7104 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7105 // There can only be one!
7106 if ( vtl->tracks_analysis_dialog )
7109 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7110 VIK_LAYER(vtl)->name,
7112 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7113 trw_layer_create_track_list,
7114 trw_layer_analyse_close );
7120 static void trw_layer_routes_stats ( gpointer lav[2] )
7122 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7123 // There can only be one!
7124 if ( vtl->tracks_analysis_dialog )
7127 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7128 VIK_LAYER(vtl)->name,
7130 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7131 trw_layer_create_track_list,
7132 trw_layer_analyse_close );
7135 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
7137 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7139 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
7142 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
7144 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7147 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7148 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7152 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
7154 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7157 if ( !strncmp(wp->comment, "http", 4) ) {
7158 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7159 } else if ( !strncmp(wp->description, "http", 4) ) {
7160 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7164 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7166 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7168 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7170 // No actual change to the name supplied
7172 if (strcmp(newname, wp->name) == 0 )
7175 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7178 // An existing waypoint has been found with the requested name
7179 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7180 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7185 // Update WP name and refresh the treeview
7186 vik_waypoint_set_name (wp, newname);
7188 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7189 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7191 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7196 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7198 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7200 // No actual change to the name supplied
7202 if (strcmp(newname, trk->name) == 0)
7205 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7208 // An existing track has been found with the requested name
7209 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7210 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7214 // Update track name and refresh GUI parts
7215 vik_track_set_name (trk, newname);
7217 // Update any subwindows that could be displaying this track which has changed name
7218 // Only one Track Edit Window
7219 if ( l->current_tp_track == trk && l->tpwin ) {
7220 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7222 // Property Dialog of the track
7223 vik_trw_layer_propwin_update ( trk );
7225 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7226 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7228 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7233 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7235 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7237 // No actual change to the name supplied
7239 if (strcmp(newname, trk->name) == 0)
7242 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7245 // An existing track has been found with the requested name
7246 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7247 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7251 // Update track name and refresh GUI parts
7252 vik_track_set_name (trk, newname);
7254 // Update any subwindows that could be displaying this track which has changed name
7255 // Only one Track Edit Window
7256 if ( l->current_tp_track == trk && l->tpwin ) {
7257 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7259 // Property Dialog of the track
7260 vik_trw_layer_propwin_update ( trk );
7262 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7263 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7265 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7272 static gboolean is_valid_geocache_name ( gchar *str )
7274 gint len = strlen ( str );
7275 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]));
7278 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7280 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7281 a_acquire_set_filter_track ( trk );
7284 #ifdef VIK_CONFIG_GOOGLE
7285 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7287 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7288 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7291 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7293 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7295 gchar *escaped = uri_escape ( tr->comment );
7296 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7297 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7304 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7305 /* viewpoint is now available instead */
7306 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7308 static gpointer pass_along[8];
7310 gboolean rv = FALSE;
7313 pass_along[1] = vlp;
7314 pass_along[2] = GINT_TO_POINTER (subtype);
7315 pass_along[3] = sublayer;
7316 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7317 pass_along[5] = vvp;
7318 pass_along[6] = iter;
7319 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7321 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7325 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7326 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7327 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7328 gtk_widget_show ( item );
7330 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7331 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7332 if (tr && tr->property_dialog)
7333 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7335 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7336 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7337 if (tr && tr->property_dialog)
7338 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7341 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7343 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7344 gtk_widget_show ( item );
7346 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7348 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7349 gtk_widget_show ( item );
7351 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7353 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7354 gtk_widget_show ( item );
7356 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7358 // Always create separator as now there is always at least the transform menu option
7359 item = gtk_menu_item_new ();
7360 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7361 gtk_widget_show ( item );
7363 /* could be a right-click using the tool */
7364 if ( vlp != NULL ) {
7365 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7366 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7368 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7369 gtk_widget_show ( item );
7372 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7374 if ( wp && wp->name ) {
7375 if ( is_valid_geocache_name ( wp->name ) ) {
7376 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7377 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7378 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7379 gtk_widget_show ( item );
7381 #ifdef VIK_CONFIG_GEOTAG
7382 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7383 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7384 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7385 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7386 gtk_widget_show ( item );
7390 if ( wp && wp->image )
7392 // Set up image paramater
7393 pass_along[5] = wp->image;
7395 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7396 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
7397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7398 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7399 gtk_widget_show ( item );
7401 #ifdef VIK_CONFIG_GEOTAG
7402 GtkWidget *geotag_submenu = gtk_menu_new ();
7403 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7404 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7405 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7406 gtk_widget_show ( item );
7407 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7409 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7410 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7411 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7412 gtk_widget_show ( item );
7414 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7415 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7416 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7417 gtk_widget_show ( item );
7423 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7424 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7425 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7426 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7428 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7429 gtk_widget_show ( item );
7435 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7436 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7438 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7439 gtk_widget_show ( item );
7440 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7441 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7442 gtk_widget_set_sensitive ( item, TRUE );
7444 gtk_widget_set_sensitive ( item, FALSE );
7447 item = gtk_menu_item_new ();
7448 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7449 gtk_widget_show ( item );
7452 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7455 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7456 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7458 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7459 gtk_widget_show ( item );
7462 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7464 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7465 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7466 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7467 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7468 gtk_widget_show ( item );
7470 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7471 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7473 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7474 gtk_widget_show ( item );
7476 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7477 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7479 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7480 gtk_widget_show ( item );
7482 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7483 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7484 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7485 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7486 gtk_widget_show ( item );
7488 GtkWidget *vis_submenu = gtk_menu_new ();
7489 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7490 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7491 gtk_widget_show ( item );
7492 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7494 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7495 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7496 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7497 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7498 gtk_widget_show ( item );
7500 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7501 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7502 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7503 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7504 gtk_widget_show ( item );
7506 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7507 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7509 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7510 gtk_widget_show ( item );
7512 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7513 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7515 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7518 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7522 if ( l->current_track && !l->current_track->is_route ) {
7523 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7525 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7526 gtk_widget_show ( item );
7528 item = gtk_menu_item_new ();
7529 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7530 gtk_widget_show ( item );
7533 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7534 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7535 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7536 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7537 gtk_widget_show ( item );
7539 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7540 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7541 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7542 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7543 gtk_widget_show ( item );
7544 // Make it available only when a new track *not* already in progress
7545 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7547 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7550 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7551 gtk_widget_show ( item );
7553 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7556 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7557 gtk_widget_show ( item );
7559 GtkWidget *vis_submenu = gtk_menu_new ();
7560 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7561 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7562 gtk_widget_show ( item );
7563 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7565 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7566 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7568 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7569 gtk_widget_show ( item );
7571 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7572 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7574 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7575 gtk_widget_show ( item );
7577 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7578 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7580 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7582 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7583 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7584 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7585 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7586 gtk_widget_show ( item );
7588 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7591 gtk_widget_show ( item );
7594 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7598 if ( l->current_track && l->current_track->is_route ) {
7599 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7600 // Reuse finish track method
7601 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7602 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7603 gtk_widget_show ( item );
7605 item = gtk_menu_item_new ();
7606 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7607 gtk_widget_show ( item );
7610 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7613 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7614 gtk_widget_show ( item );
7616 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7617 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7619 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7620 gtk_widget_show ( item );
7621 // Make it available only when a new track *not* already in progress
7622 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7624 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7625 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7626 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7627 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7628 gtk_widget_show ( item );
7630 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7631 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7632 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7633 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7634 gtk_widget_show ( item );
7636 GtkWidget *vis_submenu = gtk_menu_new ();
7637 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7638 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7639 gtk_widget_show ( item );
7640 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7645 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7646 gtk_widget_show ( item );
7648 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7651 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7652 gtk_widget_show ( item );
7654 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7655 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7657 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7659 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7660 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7662 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7664 gtk_widget_show ( item );
7666 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7667 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7668 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7669 gtk_widget_show ( item );
7673 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7674 GtkWidget *submenu_sort = gtk_menu_new ();
7675 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7676 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7677 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7678 gtk_widget_show ( item );
7679 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7681 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7682 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7683 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7684 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7685 gtk_widget_show ( item );
7687 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7688 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7689 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7690 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7691 gtk_widget_show ( item );
7694 GtkWidget *upload_submenu = gtk_menu_new ();
7696 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7698 item = gtk_menu_item_new ();
7699 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7700 gtk_widget_show ( item );
7702 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7703 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7704 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7705 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7706 if ( l->current_track ) {
7707 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7708 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7709 gtk_widget_show ( item );
7712 item = gtk_menu_item_new ();
7713 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7714 gtk_widget_show ( item );
7717 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7718 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7720 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7721 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7722 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7724 gtk_widget_show ( item );
7726 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7727 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7728 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7729 gtk_widget_show ( item );
7731 GtkWidget *goto_submenu;
7732 goto_submenu = gtk_menu_new ();
7733 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7734 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7735 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7736 gtk_widget_show ( item );
7737 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7739 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7740 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7741 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7742 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7743 gtk_widget_show ( item );
7745 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7746 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7748 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7749 gtk_widget_show ( item );
7751 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7752 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7754 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7755 gtk_widget_show ( item );
7757 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7758 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7759 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7760 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7761 gtk_widget_show ( item );
7763 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7764 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7765 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7766 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7767 gtk_widget_show ( item );
7769 // Routes don't have speeds
7770 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7771 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7772 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7773 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7774 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7775 gtk_widget_show ( item );
7778 GtkWidget *combine_submenu;
7779 combine_submenu = gtk_menu_new ();
7780 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7781 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7782 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7783 gtk_widget_show ( item );
7784 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7786 // Routes don't have times or segments...
7787 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7788 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7789 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7790 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7791 gtk_widget_show ( item );
7793 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7795 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7796 gtk_widget_show ( item );
7799 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7800 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7801 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7802 gtk_widget_show ( item );
7804 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7805 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7807 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7809 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7810 gtk_widget_show ( item );
7812 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7813 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7815 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7817 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7818 gtk_widget_show ( item );
7820 GtkWidget *split_submenu;
7821 split_submenu = gtk_menu_new ();
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7824 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7825 gtk_widget_show ( item );
7826 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7828 // Routes don't have times or segments...
7829 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7830 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7832 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7833 gtk_widget_show ( item );
7835 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7836 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7837 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7838 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7839 gtk_widget_show ( item );
7842 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7843 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7844 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7845 gtk_widget_show ( item );
7847 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7848 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7849 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7850 gtk_widget_show ( item );
7851 // Make it available only when a trackpoint is selected.
7852 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7854 GtkWidget *insert_submenu = gtk_menu_new ();
7855 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7856 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7857 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7858 gtk_widget_show ( item );
7859 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7861 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7863 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7864 gtk_widget_show ( item );
7865 // Make it available only when a point is selected
7866 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7868 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7870 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7871 gtk_widget_show ( item );
7872 // Make it available only when a point is selected
7873 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7875 GtkWidget *delete_submenu;
7876 delete_submenu = gtk_menu_new ();
7877 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7878 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7879 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7880 gtk_widget_show ( item );
7881 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7883 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7884 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7885 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7886 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7887 gtk_widget_show ( item );
7888 // Make it available only when a point is selected
7889 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7891 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7893 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7894 gtk_widget_show ( item );
7896 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7898 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7899 gtk_widget_show ( item );
7901 GtkWidget *transform_submenu;
7902 transform_submenu = gtk_menu_new ();
7903 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7904 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7905 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7906 gtk_widget_show ( item );
7907 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7909 GtkWidget *dem_submenu;
7910 dem_submenu = gtk_menu_new ();
7911 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7912 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
7913 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7914 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7916 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7917 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7918 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7919 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7920 gtk_widget_show ( item );
7922 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7924 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7925 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7926 gtk_widget_show ( item );
7928 GtkWidget *smooth_submenu;
7929 smooth_submenu = gtk_menu_new ();
7930 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7931 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7932 gtk_widget_show ( item );
7933 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7935 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7937 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7938 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7939 gtk_widget_show ( item );
7941 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7942 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7943 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7944 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7945 gtk_widget_show ( item );
7947 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7948 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7950 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7951 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7954 gtk_widget_show ( item );
7956 // Routes don't have timestamps - so this is only available for tracks
7957 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7960 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7961 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7962 gtk_widget_show ( item );
7965 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7966 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7968 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7969 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7970 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7971 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7972 gtk_widget_show ( item );
7974 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7975 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7976 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7977 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7978 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7979 gtk_widget_show ( item );
7982 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7984 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7985 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7987 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7988 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
7989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7990 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7991 gtk_widget_show ( item );
7994 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7995 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7998 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8001 gtk_widget_show ( item );
8003 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8004 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8006 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8007 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8009 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8010 gtk_widget_show ( item );
8012 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8013 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8014 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
8015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8016 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8017 gtk_widget_show ( item );
8020 // ATM can't upload a single waypoint but can do waypoints to a GPS
8021 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8022 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8023 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8024 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8025 gtk_widget_show ( item );
8026 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8028 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8031 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8032 gtk_widget_show ( item );
8036 #ifdef VIK_CONFIG_GOOGLE
8037 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8039 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8040 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8042 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8043 gtk_widget_show ( item );
8047 // Some things aren't usable with routes
8048 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8049 #ifdef VIK_CONFIG_OPENSTREETMAP
8050 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8051 // Convert internal pointer into actual track for usage outside this file
8052 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
8053 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8056 gtk_widget_show ( item );
8059 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8062 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8063 gtk_widget_show ( item );
8065 /* ATM This function is only available via the layers panel, due to needing a vlp */
8067 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8068 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8069 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8071 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8072 gtk_widget_show ( item );
8076 #ifdef VIK_CONFIG_GEOTAG
8077 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8078 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8079 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8080 gtk_widget_show ( item );
8084 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8085 // Only show on viewport popmenu when a trackpoint is selected
8086 if ( ! vlp && l->current_tpl ) {
8088 item = gtk_menu_item_new ();
8089 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8090 gtk_widget_show ( item );
8092 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8093 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8094 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8095 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8096 gtk_widget_show ( item );
8100 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8101 GtkWidget *transform_submenu;
8102 transform_submenu = gtk_menu_new ();
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8105 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8106 gtk_widget_show ( item );
8107 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8109 GtkWidget *dem_submenu;
8110 dem_submenu = gtk_menu_new ();
8111 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8112 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
8113 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8114 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8116 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8117 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8118 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8119 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8120 gtk_widget_show ( item );
8122 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8124 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8125 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8126 gtk_widget_show ( item );
8129 gtk_widget_show_all ( GTK_WIDGET(menu) );
8134 // TODO: Probably better to rework this track manipulation in viktrack.c
8135 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8138 if (!vtl->current_tpl)
8141 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8142 VikTrackpoint *tp_other = NULL;
8145 if (!vtl->current_tpl->prev)
8147 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8149 if (!vtl->current_tpl->next)
8151 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8154 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8157 VikTrackpoint *tp_new = vik_trackpoint_new();
8158 struct LatLon ll_current, ll_other;
8159 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8160 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8162 /* main positional interpolation */
8163 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8164 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8166 /* Now other properties that can be interpolated */
8167 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8169 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8170 /* Note here the division is applied to each part, then added
8171 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8172 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8173 tp_new->has_timestamp = TRUE;
8176 if (tp_current->speed != NAN && tp_other->speed != NAN)
8177 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8179 /* TODO - improve interpolation of course, as it may not be correct.
8180 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8181 [similar applies if value is in radians] */
8182 if (tp_current->course != NAN && tp_other->course != NAN)
8183 tp_new->course = (tp_current->course + tp_other->course)/2;
8185 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8187 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8188 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8190 // Otherwise try routes
8191 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8195 gint index = g_list_index ( trk->trackpoints, tp_current );
8199 // NB no recalculation of bounds since it is inserted between points
8200 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8205 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8211 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8215 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8217 if ( vtl->current_tpl )
8219 vtl->current_tpl = NULL;
8220 vtl->current_tp_track = NULL;
8221 vtl->current_tp_id = NULL;
8222 vik_layer_emit_update(VIK_LAYER(vtl));
8226 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8228 g_assert ( vtl->tpwin != NULL );
8229 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8230 trw_layer_cancel_current_tp ( vtl, TRUE );
8232 if ( vtl->current_tpl == NULL )
8235 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8237 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8238 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8240 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8242 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8244 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8248 trw_layer_trackpoint_selected_delete ( vtl, tr );
8250 if ( vtl->current_tpl )
8251 // Reset dialog with the available adjacent trackpoint
8252 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8254 vik_layer_emit_update(VIK_LAYER(vtl));
8256 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8258 if ( vtl->current_tp_track )
8259 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8260 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8262 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8264 if ( vtl->current_tp_track )
8265 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8266 vik_layer_emit_update(VIK_LAYER(vtl));
8268 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8270 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8271 vik_layer_emit_update(VIK_LAYER(vtl));
8273 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8274 vik_layer_emit_update(VIK_LAYER(vtl));
8278 * trw_layer_dialog_shift:
8279 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8281 * Try to reposition a dialog if it's over the specified coord
8282 * so to not obscure the item of interest
8284 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8286 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8288 // Attempt force dialog to be shown so we can find out where it is more reliably...
8289 while ( gtk_events_pending() )
8290 gtk_main_iteration ();
8292 // get parent window position & size
8293 gint win_pos_x, win_pos_y;
8294 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8296 gint win_size_x, win_size_y;
8297 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8299 // get own dialog size
8300 gint dia_size_x, dia_size_y;
8301 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8303 // get own dialog position
8304 gint dia_pos_x, dia_pos_y;
8305 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8307 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8308 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8310 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8312 gint vp_xx, vp_yy; // In viewport pixels
8313 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8315 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8319 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8321 // Transform Viewport pixels into absolute pixels
8322 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8323 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8325 // Is dialog over the point (to within an ^^ edge value)
8326 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8327 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8331 gint hh = vik_viewport_get_height ( vvp );
8333 // Consider the difference in viewport to the full window
8334 gint offset_y = dest_y;
8335 // Add difference between dialog and window sizes
8336 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8338 if ( vp_yy > hh/2 ) {
8339 // Point in bottom half, move window to top half
8340 gtk_window_move ( dialog, dia_pos_x, offset_y );
8343 // Point in top half, move dialog down
8344 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8348 // Shift left<->right
8349 gint ww = vik_viewport_get_width ( vvp );
8351 // Consider the difference in viewport to the full window
8352 gint offset_x = dest_x;
8353 // Add difference between dialog and window sizes
8354 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8356 if ( vp_xx > ww/2 ) {
8357 // Point on right, move window to left
8358 gtk_window_move ( dialog, offset_x, dia_pos_y );
8361 // Point on left, move right
8362 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8374 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8375 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8376 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8377 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8379 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8381 if ( vtl->current_tpl ) {
8382 // get tp pixel position
8383 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8385 // Shift up<->down to try not to obscure the trackpoint.
8386 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8390 if ( vtl->current_tpl )
8391 if ( vtl->current_tp_track )
8392 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8393 /* set layer name and TP data */
8396 /***************************************************************************
8398 ***************************************************************************/
8400 /*** Utility data structures and functions ****/
8404 gint closest_x, closest_y;
8405 gboolean draw_images;
8406 gpointer *closest_wp_id;
8407 VikWaypoint *closest_wp;
8413 gint closest_x, closest_y;
8414 gpointer closest_track_id;
8415 VikTrackpoint *closest_tp;
8421 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8427 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8429 // If waypoint has an image then use the image size to select
8430 if ( params->draw_images && wp->image ) {
8431 gint slackx, slacky;
8432 slackx = wp->image_width / 2;
8433 slacky = wp->image_height / 2;
8435 if ( x <= params->x + slackx && x >= params->x - slackx
8436 && y <= params->y + slacky && y >= params->y - slacky ) {
8437 params->closest_wp_id = id;
8438 params->closest_wp = wp;
8439 params->closest_x = x;
8440 params->closest_y = y;
8443 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8444 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8445 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8447 params->closest_wp_id = id;
8448 params->closest_wp = wp;
8449 params->closest_x = x;
8450 params->closest_y = y;
8454 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8456 GList *tpl = t->trackpoints;
8462 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8468 tp = VIK_TRACKPOINT(tpl->data);
8470 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8472 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8473 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8474 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8476 params->closest_track_id = id;
8477 params->closest_tp = tp;
8478 params->closest_tpl = tpl;
8479 params->closest_x = x;
8480 params->closest_y = y;
8486 // ATM: Leave this as 'Track' only.
8487 // Not overly bothered about having a snap to route trackpoint capability
8488 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8490 TPSearchParams params;
8494 params.closest_track_id = NULL;
8495 params.closest_tp = NULL;
8496 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8497 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8498 return params.closest_tp;
8501 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8503 WPSearchParams params;
8507 params.draw_images = vtl->drawimages;
8508 params.closest_wp = NULL;
8509 params.closest_wp_id = NULL;
8510 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8511 return params.closest_wp;
8515 // Some forward declarations
8516 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8517 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8518 static void marker_end_move ( tool_ed_t *t );
8521 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8525 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8527 // Here always allow snapping back to the original location
8528 // this is useful when one decides not to move the thing afterall
8529 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8532 if ( event->state & GDK_CONTROL_MASK )
8534 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8536 new_coord = tp->coord;
8540 if ( event->state & GDK_SHIFT_MASK )
8542 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8544 new_coord = wp->coord;
8548 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8550 marker_moveto ( t, x, y );
8557 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8559 if ( t->holding && event->button == 1 )
8562 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8565 if ( event->state & GDK_CONTROL_MASK )
8567 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8569 new_coord = tp->coord;
8573 if ( event->state & GDK_SHIFT_MASK )
8575 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8577 new_coord = wp->coord;
8580 marker_end_move ( t );
8582 // Determine if working on a waypoint or a trackpoint
8583 if ( t->is_waypoint ) {
8584 // Update waypoint position
8585 vtl->current_wp->coord = new_coord;
8586 trw_layer_calculate_bounds_waypoints ( vtl );
8587 // Reset waypoint pointer
8588 vtl->current_wp = NULL;
8589 vtl->current_wp_id = NULL;
8592 if ( vtl->current_tpl ) {
8593 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8595 if ( vtl->current_tp_track )
8596 vik_track_calculate_bounds ( vtl->current_tp_track );
8599 if ( vtl->current_tp_track )
8600 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8601 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8605 vik_layer_emit_update ( VIK_LAYER(vtl) );
8612 Returns true if a waypoint or track is found near the requested event position for this particular layer
8613 The item found is automatically selected
8614 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8616 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8618 if ( event->button != 1 )
8621 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8624 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8628 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8630 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8632 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8633 WPSearchParams wp_params;
8634 wp_params.vvp = vvp;
8635 wp_params.x = event->x;
8636 wp_params.y = event->y;
8637 wp_params.draw_images = vtl->drawimages;
8638 wp_params.closest_wp_id = NULL;
8639 wp_params.closest_wp = NULL;
8641 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8643 if ( wp_params.closest_wp ) {
8646 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8648 // Too easy to move it so must be holding shift to start immediately moving it
8649 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8650 if ( event->state & GDK_SHIFT_MASK ||
8651 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8652 // Put into 'move buffer'
8653 // NB vvp & vw already set in tet
8654 tet->vtl = (gpointer)vtl;
8655 tet->is_waypoint = TRUE;
8657 marker_begin_move (tet, event->x, event->y);
8660 vtl->current_wp = wp_params.closest_wp;
8661 vtl->current_wp_id = wp_params.closest_wp_id;
8663 vik_layer_emit_update ( VIK_LAYER(vtl) );
8669 // Used for both track and route lists
8670 TPSearchParams tp_params;
8671 tp_params.vvp = vvp;
8672 tp_params.x = event->x;
8673 tp_params.y = event->y;
8674 tp_params.closest_track_id = NULL;
8675 tp_params.closest_tp = NULL;
8676 tp_params.closest_tpl = NULL;
8677 tp_params.bbox = bbox;
8679 if (vtl->tracks_visible) {
8680 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8682 if ( tp_params.closest_tp ) {
8684 // Always select + highlight the track
8685 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8687 tet->is_waypoint = FALSE;
8689 // Select the Trackpoint
8690 // Can move it immediately when control held or it's the previously selected tp
8691 if ( event->state & GDK_CONTROL_MASK ||
8692 vtl->current_tpl == tp_params.closest_tpl ) {
8693 // Put into 'move buffer'
8694 // NB vvp & vw already set in tet
8695 tet->vtl = (gpointer)vtl;
8696 marker_begin_move (tet, event->x, event->y);
8699 vtl->current_tpl = tp_params.closest_tpl;
8700 vtl->current_tp_id = tp_params.closest_track_id;
8701 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8703 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8706 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8708 vik_layer_emit_update ( VIK_LAYER(vtl) );
8713 // Try again for routes
8714 if (vtl->routes_visible) {
8715 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8717 if ( tp_params.closest_tp ) {
8719 // Always select + highlight the track
8720 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8722 tet->is_waypoint = FALSE;
8724 // Select the Trackpoint
8725 // Can move it immediately when control held or it's the previously selected tp
8726 if ( event->state & GDK_CONTROL_MASK ||
8727 vtl->current_tpl == tp_params.closest_tpl ) {
8728 // Put into 'move buffer'
8729 // NB vvp & vw already set in tet
8730 tet->vtl = (gpointer)vtl;
8731 marker_begin_move (tet, event->x, event->y);
8734 vtl->current_tpl = tp_params.closest_tpl;
8735 vtl->current_tp_id = tp_params.closest_track_id;
8736 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8738 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8741 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8743 vik_layer_emit_update ( VIK_LAYER(vtl) );
8748 /* these aren't the droids you're looking for */
8749 vtl->current_wp = NULL;
8750 vtl->current_wp_id = NULL;
8751 trw_layer_cancel_current_tp ( vtl, FALSE );
8754 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8759 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8761 if ( event->button != 3 )
8764 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8767 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8770 /* Post menu for the currently selected item */
8772 /* See if a track is selected */
8773 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8774 if ( track && track->visible ) {
8776 if ( track->name ) {
8778 if ( vtl->track_right_click_menu )
8779 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8781 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8788 if ( track->is_route )
8789 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8791 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8793 if ( trkf && udataU.uuid ) {
8796 if ( track->is_route )
8797 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8799 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8801 trw_layer_sublayer_add_menu_items ( vtl,
8802 vtl->track_right_click_menu,
8804 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8810 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8816 /* See if a waypoint is selected */
8817 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8818 if ( waypoint && waypoint->visible ) {
8819 if ( waypoint->name ) {
8821 if ( vtl->wp_right_click_menu )
8822 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8824 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8827 udata.wp = waypoint;
8830 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8832 if ( wpf && udata.uuid ) {
8833 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8835 trw_layer_sublayer_add_menu_items ( vtl,
8836 vtl->wp_right_click_menu,
8838 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8843 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8852 /* background drawing hook, to be passed the viewport */
8853 static gboolean tool_sync_done = TRUE;
8855 static gboolean tool_sync(gpointer data)
8857 VikViewport *vvp = data;
8858 gdk_threads_enter();
8859 vik_viewport_sync(vvp);
8860 tool_sync_done = TRUE;
8861 gdk_threads_leave();
8865 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8868 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8869 gdk_gc_set_function ( t->gc, GDK_INVERT );
8870 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8871 vik_viewport_sync(t->vvp);
8876 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8878 VikViewport *vvp = t->vvp;
8879 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8880 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8884 if (tool_sync_done) {
8885 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8886 tool_sync_done = FALSE;
8890 static void marker_end_move ( tool_ed_t *t )
8892 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8893 g_object_unref ( t->gc );
8897 /*** Edit waypoint ****/
8899 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8901 tool_ed_t *t = g_new(tool_ed_t, 1);
8907 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8912 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8914 WPSearchParams params;
8915 tool_ed_t *t = data;
8916 VikViewport *vvp = t->vvp;
8918 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8925 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8928 if ( vtl->current_wp && vtl->current_wp->visible )
8930 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8932 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8934 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8935 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8937 if ( event->button == 3 )
8938 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8940 marker_begin_move(t, event->x, event->y);
8947 params.x = event->x;
8948 params.y = event->y;
8949 params.draw_images = vtl->drawimages;
8950 params.closest_wp_id = NULL;
8951 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8952 params.closest_wp = NULL;
8953 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8954 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8956 // how do we get here?
8957 marker_begin_move(t, event->x, event->y);
8958 g_critical("shouldn't be here");
8961 else if ( params.closest_wp )
8963 if ( event->button == 3 )
8964 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8966 vtl->waypoint_rightclick = FALSE;
8968 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8970 vtl->current_wp = params.closest_wp;
8971 vtl->current_wp_id = params.closest_wp_id;
8973 /* could make it so don't update if old WP is off screen and new is null but oh well */
8974 vik_layer_emit_update ( VIK_LAYER(vtl) );
8978 vtl->current_wp = NULL;
8979 vtl->current_wp_id = NULL;
8980 vtl->waypoint_rightclick = FALSE;
8981 vik_layer_emit_update ( VIK_LAYER(vtl) );
8985 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8987 tool_ed_t *t = data;
8988 VikViewport *vvp = t->vvp;
8990 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8995 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8998 if ( event->state & GDK_CONTROL_MASK )
9000 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9002 new_coord = tp->coord;
9006 if ( event->state & GDK_SHIFT_MASK )
9008 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9009 if ( wp && wp != vtl->current_wp )
9010 new_coord = wp->coord;
9015 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9017 marker_moveto ( t, x, y );
9024 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9026 tool_ed_t *t = data;
9027 VikViewport *vvp = t->vvp;
9029 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9032 if ( t->holding && event->button == 1 )
9035 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9038 if ( event->state & GDK_CONTROL_MASK )
9040 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9042 new_coord = tp->coord;
9046 if ( event->state & GDK_SHIFT_MASK )
9048 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9049 if ( wp && wp != vtl->current_wp )
9050 new_coord = wp->coord;
9053 marker_end_move ( t );
9055 vtl->current_wp->coord = new_coord;
9057 trw_layer_calculate_bounds_waypoints ( vtl );
9058 vik_layer_emit_update ( VIK_LAYER(vtl) );
9061 /* PUT IN RIGHT PLACE!!! */
9062 if ( event->button == 3 && vtl->waypoint_rightclick )
9064 if ( vtl->wp_right_click_menu )
9065 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9066 if ( vtl->current_wp ) {
9067 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9068 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 );
9069 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9071 vtl->waypoint_rightclick = FALSE;
9076 /*** New track ****/
9078 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9085 GdkDrawable *drawable;
9091 * Draw specified pixmap
9093 static gboolean draw_sync ( gpointer data )
9095 draw_sync_t *ds = (draw_sync_t*) data;
9096 // Sometimes don't want to draw
9097 // normally because another update has taken precedent such as panning the display
9098 // which means this pixmap is no longer valid
9099 if ( ds->vtl->draw_sync_do ) {
9100 gdk_threads_enter();
9101 gdk_draw_drawable (ds->drawable,
9104 0, 0, 0, 0, -1, -1);
9105 ds->vtl->draw_sync_done = TRUE;
9106 gdk_threads_leave();
9112 static gchar* distance_string (gdouble distance)
9116 /* draw label with distance */
9117 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9118 switch (dist_units) {
9119 case VIK_UNITS_DISTANCE_MILES:
9120 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9121 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9122 } else if (distance < 1609.4) {
9123 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9125 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9129 // VIK_UNITS_DISTANCE_KILOMETRES
9130 if (distance >= 1000 && distance < 100000) {
9131 g_sprintf(str, "%3.2f km", distance/1000.0);
9132 } else if (distance < 1000) {
9133 g_sprintf(str, "%d m", (int)distance);
9135 g_sprintf(str, "%d km", (int)distance/1000);
9139 return g_strdup (str);
9143 * Actually set the message in statusbar
9145 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9147 // Only show elevation data when track has some elevation properties
9148 gchar str_gain_loss[64];
9149 str_gain_loss[0] = '\0';
9150 gchar str_last_step[64];
9151 str_last_step[0] = '\0';
9152 gchar *str_total = distance_string (distance);
9154 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9155 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9156 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9158 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9161 if ( last_step > 0 ) {
9162 gchar *tmp = distance_string (last_step);
9163 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9167 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9169 // Write with full gain/loss information
9170 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9171 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9173 g_free ( str_total );
9177 * Figure out what information should be set in the statusbar and then write it
9179 static void update_statusbar ( VikTrwLayer *vtl )
9181 // Get elevation data
9182 gdouble elev_gain, elev_loss;
9183 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9185 /* Find out actual distance of current track */
9186 gdouble distance = vik_track_get_length (vtl->current_track);
9188 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9192 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9194 /* if we haven't sync'ed yet, we don't have time to do more. */
9195 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9196 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9197 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9199 static GdkPixmap *pixmap = NULL;
9201 // Need to check in case window has been resized
9202 w1 = vik_viewport_get_width(vvp);
9203 h1 = vik_viewport_get_height(vvp);
9205 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9207 gdk_drawable_get_size (pixmap, &w2, &h2);
9208 if (w1 != w2 || h1 != h2) {
9209 g_object_unref ( G_OBJECT ( pixmap ) );
9210 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9213 // Reset to background
9214 gdk_draw_drawable (pixmap,
9215 vtl->current_track_newpoint_gc,
9216 vik_viewport_get_pixmap(vvp),
9217 0, 0, 0, 0, -1, -1);
9219 draw_sync_t *passalong;
9222 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9224 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9225 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9226 // thus when we come to reset to the background it would include what we have already drawn!!
9227 gdk_draw_line ( pixmap,
9228 vtl->current_track_newpoint_gc,
9229 x1, y1, event->x, event->y );
9230 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9232 /* Find out actual distance of current track */
9233 gdouble distance = vik_track_get_length (vtl->current_track);
9235 // Now add distance to where the pointer is //
9238 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9239 vik_coord_to_latlon ( &coord, &ll );
9240 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9241 distance = distance + last_step;
9243 // Get elevation data
9244 gdouble elev_gain, elev_loss;
9245 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9247 // Adjust elevation data (if available) for the current pointer position
9249 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9250 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9251 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9252 // Adjust elevation of last track point
9253 if ( elev_new > last_tpt->altitude )
9255 elev_gain += elev_new - last_tpt->altitude;
9258 elev_loss += last_tpt->altitude - elev_new;
9263 // Display of the distance 'tooltip' during track creation is controlled by a preference
9265 if ( a_vik_get_create_track_tooltip() ) {
9267 gchar *str = distance_string (distance);
9269 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9270 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9271 pango_layout_set_text (pl, str, -1);
9273 pango_layout_get_pixel_size ( pl, &wd, &hd );
9276 // offset from cursor a bit depending on font size
9280 // Create a background block to make the text easier to read over the background map
9281 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9282 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9283 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9285 g_object_unref ( G_OBJECT ( pl ) );
9286 g_object_unref ( G_OBJECT ( background_block_gc ) );
9290 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9291 passalong->vtl = vtl;
9292 passalong->pixmap = pixmap;
9293 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9294 passalong->gc = vtl->current_track_newpoint_gc;
9298 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9300 // Update statusbar with full gain/loss information
9301 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9303 // draw pixmap when we have time to
9304 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9305 vtl->draw_sync_done = FALSE;
9306 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9308 return VIK_LAYER_TOOL_ACK;
9311 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9313 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9314 vtl->current_track = NULL;
9315 vik_layer_emit_update ( VIK_LAYER(vtl) );
9317 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9319 if ( vtl->current_track->trackpoints )
9321 GList *last = g_list_last(vtl->current_track->trackpoints);
9322 g_free ( last->data );
9323 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9326 update_statusbar ( vtl );
9328 vik_layer_emit_update ( VIK_LAYER(vtl) );
9335 * Common function to handle trackpoint button requests on either a route or a track
9336 * . enables adding a point via normal click
9337 * . enables removal of last point via right click
9338 * . finishing of the track or route via double clicking
9340 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9344 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9347 if ( event->button == 2 ) {
9348 // As the display is panning, the new track pixmap is now invalid so don't draw it
9349 // otherwise this drawing done results in flickering back to an old image
9350 vtl->draw_sync_do = FALSE;
9354 if ( event->button == 3 )
9356 if ( !vtl->current_track )
9359 if ( vtl->current_track->trackpoints )
9361 GList *last = g_list_last(vtl->current_track->trackpoints);
9362 g_free ( last->data );
9363 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9365 vik_track_calculate_bounds ( vtl->current_track );
9366 update_statusbar ( vtl );
9368 vik_layer_emit_update ( VIK_LAYER(vtl) );
9372 if ( event->type == GDK_2BUTTON_PRESS )
9374 /* subtract last (duplicate from double click) tp then end */
9375 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9377 GList *last = g_list_last(vtl->current_track->trackpoints);
9378 g_free ( last->data );
9379 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9380 /* undo last, then end */
9381 vtl->current_track = NULL;
9383 vik_layer_emit_update ( VIK_LAYER(vtl) );
9387 tp = vik_trackpoint_new();
9388 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9390 /* snap to other TP */
9391 if ( event->state & GDK_CONTROL_MASK )
9393 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9395 tp->coord = other_tp->coord;
9398 tp->newsegment = FALSE;
9399 tp->has_timestamp = FALSE;
9402 if ( vtl->current_track ) {
9403 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9404 /* Auto attempt to get elevation from DEM data (if it's available) */
9405 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9408 vtl->ct_x1 = vtl->ct_x2;
9409 vtl->ct_y1 = vtl->ct_y2;
9410 vtl->ct_x2 = event->x;
9411 vtl->ct_y2 = event->y;
9413 vik_layer_emit_update ( VIK_LAYER(vtl) );
9417 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9419 // ----------------------------------------------------- if current is a route - switch to new track
9420 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9422 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9423 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9425 new_track_create_common ( vtl, name );
9431 return tool_new_track_or_route_click ( vtl, event, vvp );
9434 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9436 if ( event->button == 2 ) {
9437 // Pan moving ended - enable potential point drawing again
9438 vtl->draw_sync_do = TRUE;
9439 vtl->draw_sync_done = TRUE;
9443 /*** New route ****/
9445 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9450 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9452 // -------------------------- if current is a track - switch to new route
9453 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9455 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9456 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9457 new_route_create_common ( vtl, name );
9463 return tool_new_track_or_route_click ( vtl, event, vvp );
9466 /*** New waypoint ****/
9468 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9473 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9476 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9478 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9479 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9480 trw_layer_calculate_bounds_waypoints ( vtl );
9481 vik_layer_emit_update ( VIK_LAYER(vtl) );
9487 /*** Edit trackpoint ****/
9489 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9491 tool_ed_t *t = g_new(tool_ed_t, 1);
9497 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9502 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9504 tool_ed_t *t = data;
9505 VikViewport *vvp = t->vvp;
9506 TPSearchParams params;
9507 /* OUTDATED DOCUMENTATION:
9508 find 5 pixel range on each side. then put these UTM, and a pointer
9509 to the winning track name (and maybe the winning track itself), and a
9510 pointer to the winning trackpoint, inside an array or struct. pass
9511 this along, do a foreach on the tracks which will do a foreach on the
9514 params.x = event->x;
9515 params.y = event->y;
9516 params.closest_track_id = NULL;
9517 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9518 params.closest_tp = NULL;
9519 params.closest_tpl = NULL;
9520 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9522 if ( event->button != 1 )
9525 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9528 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9531 if ( vtl->current_tpl )
9533 /* 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.) */
9534 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9535 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9540 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9542 if ( current_tr->visible &&
9543 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9544 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9545 marker_begin_move ( t, event->x, event->y );
9551 if ( vtl->tracks_visible )
9552 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9554 if ( params.closest_tp )
9556 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9557 vtl->current_tpl = params.closest_tpl;
9558 vtl->current_tp_id = params.closest_track_id;
9559 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9560 trw_layer_tpwin_init ( vtl );
9561 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9562 vik_layer_emit_update ( VIK_LAYER(vtl) );
9566 if ( vtl->routes_visible )
9567 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9569 if ( params.closest_tp )
9571 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9572 vtl->current_tpl = params.closest_tpl;
9573 vtl->current_tp_id = params.closest_track_id;
9574 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9575 trw_layer_tpwin_init ( vtl );
9576 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9577 vik_layer_emit_update ( VIK_LAYER(vtl) );
9581 /* these aren't the droids you're looking for */
9585 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9587 tool_ed_t *t = data;
9588 VikViewport *vvp = t->vvp;
9590 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9596 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9599 if ( event->state & GDK_CONTROL_MASK )
9601 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9602 if ( tp && tp != vtl->current_tpl->data )
9603 new_coord = tp->coord;
9605 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9608 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9609 marker_moveto ( t, x, y );
9617 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9619 tool_ed_t *t = data;
9620 VikViewport *vvp = t->vvp;
9622 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9624 if ( event->button != 1)
9629 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9632 if ( event->state & GDK_CONTROL_MASK )
9634 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9635 if ( tp && tp != vtl->current_tpl->data )
9636 new_coord = tp->coord;
9639 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9640 if ( vtl->current_tp_track )
9641 vik_track_calculate_bounds ( vtl->current_tp_track );
9643 marker_end_move ( t );
9645 /* diff dist is diff from orig */
9647 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9649 vik_layer_emit_update ( VIK_LAYER(vtl) );
9656 /*** Route Finder ***/
9657 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9662 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9665 if ( !vtl ) return FALSE;
9666 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9667 if ( event->button == 3 && vtl->route_finder_current_track ) {
9669 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9671 vtl->route_finder_coord = *new_end;
9673 vik_layer_emit_update ( VIK_LAYER(vtl) );
9674 /* remove last ' to:...' */
9675 if ( vtl->route_finder_current_track->comment ) {
9676 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9677 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9678 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9679 last_to - vtl->route_finder_current_track->comment - 1);
9680 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9685 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9686 struct LatLon start, end;
9688 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9689 vik_coord_to_latlon ( &(tmp), &end );
9690 vtl->route_finder_coord = tmp; /* for continuations */
9692 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9693 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9694 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9696 vtl->route_finder_check_added_track = TRUE;
9697 vtl->route_finder_started = FALSE;
9700 vik_routing_default_find ( vtl, start, end);
9702 /* see if anything was done -- a track was added or appended to */
9703 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9704 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 ) );
9705 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9706 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9707 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9708 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9711 if ( vtl->route_finder_added_track )
9712 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9714 vtl->route_finder_added_track = NULL;
9715 vtl->route_finder_check_added_track = FALSE;
9716 vtl->route_finder_append = FALSE;
9718 vik_layer_emit_update ( VIK_LAYER(vtl) );
9720 vtl->route_finder_started = TRUE;
9721 vtl->route_finder_coord = tmp;
9722 vtl->route_finder_current_track = NULL;
9727 /*** Show picture ****/
9729 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9734 /* Params are: vvp, event, last match found or NULL */
9735 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9737 if ( wp->image && wp->visible )
9739 gint x, y, slackx, slacky;
9740 GdkEventButton *event = (GdkEventButton *) params[1];
9742 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9743 slackx = wp->image_width / 2;
9744 slacky = wp->image_height / 2;
9745 if ( x <= event->x + slackx && x >= event->x - slackx
9746 && y <= event->y + slacky && y >= event->y - slacky )
9748 params[2] = wp->image; /* we've found a match. however continue searching
9749 * since we want to find the last match -- that
9750 * is, the match that was drawn last. */
9755 static void trw_layer_show_picture ( gpointer pass_along[6] )
9757 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9759 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9762 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9763 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9764 g_free ( quoted_file );
9765 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9767 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() );
9768 g_error_free ( err );
9771 #endif /* WINDOWS */
9774 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9776 gpointer params[3] = { vvp, event, NULL };
9777 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9779 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9782 static gpointer pass_along[6];
9783 pass_along[0] = vtl;
9784 pass_along[5] = params[2];
9785 trw_layer_show_picture ( pass_along );
9786 return TRUE; /* found a match */
9789 return FALSE; /* go through other layers, searching for a match */
9792 /***************************************************************************
9794 ***************************************************************************/
9797 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9799 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9800 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9803 /* Structure for thumbnail creating data used in the background thread */
9805 VikTrwLayer *vtl; // Layer needed for redrawing
9806 GSList *pics; // Image list
9807 } thumbnail_create_thread_data;
9809 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9811 guint total = g_slist_length(tctd->pics), done = 0;
9812 while ( tctd->pics )
9814 a_thumbnails_create ( (gchar *) tctd->pics->data );
9815 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9817 return -1; /* Abort thread */
9819 tctd->pics = tctd->pics->next;
9822 // Redraw to show the thumbnails as they are now created
9823 if ( IS_VIK_LAYER(tctd->vtl) )
9824 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9829 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9831 while ( tctd->pics )
9833 g_free ( tctd->pics->data );
9834 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9839 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9841 if ( ! vtl->has_verified_thumbnails )
9843 GSList *pics = NULL;
9844 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9847 gint len = g_slist_length ( pics );
9848 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9849 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9852 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9854 (vik_thr_func) create_thumbnails_thread,
9856 (vik_thr_free_func) thumbnail_create_thread_free,
9864 static const gchar* my_track_colors ( gint ii )
9866 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9878 // Fast and reliable way of returning a colour
9879 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9882 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9884 GHashTableIter iter;
9885 gpointer key, value;
9889 g_hash_table_iter_init ( &iter, vtl->tracks );
9891 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9893 // Tracks get a random spread of colours if not already assigned
9894 if ( ! VIK_TRACK(value)->has_color ) {
9895 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9896 VIK_TRACK(value)->color = vtl->track_color;
9898 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9900 VIK_TRACK(value)->has_color = TRUE;
9903 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9906 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9912 g_hash_table_iter_init ( &iter, vtl->routes );
9914 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9916 // Routes get an intermix of reds
9917 if ( ! VIK_TRACK(value)->has_color ) {
9919 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9921 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9922 VIK_TRACK(value)->has_color = TRUE;
9925 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9932 * (Re)Calculate the bounds of the waypoints in this layer,
9933 * This should be called whenever waypoints are changed
9935 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9937 struct LatLon topleft = { 0.0, 0.0 };
9938 struct LatLon bottomright = { 0.0, 0.0 };
9941 GHashTableIter iter;
9942 gpointer key, value;
9944 g_hash_table_iter_init ( &iter, vtl->waypoints );
9946 // Set bounds to first point
9947 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9948 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9949 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9952 // Ensure there is another point...
9953 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9955 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9957 // See if this point increases the bounds.
9958 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9960 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9961 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9962 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9963 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9967 vtl->waypoints_bbox.north = topleft.lat;
9968 vtl->waypoints_bbox.east = bottomright.lon;
9969 vtl->waypoints_bbox.south = bottomright.lat;
9970 vtl->waypoints_bbox.west = topleft.lon;
9973 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9975 vik_track_calculate_bounds ( trk );
9978 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9980 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9981 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9984 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9986 if ( ! VIK_LAYER(vtl)->vt )
9989 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9990 if ( g_hash_table_size (vtl->tracks) > 1 )
9991 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9993 if ( g_hash_table_size (vtl->routes) > 1 )
9994 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9996 if ( g_hash_table_size (vtl->waypoints) > 1 )
9997 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10000 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10002 if ( VIK_LAYER(vtl)->realized )
10003 trw_layer_verify_thumbnails ( vtl, vvp );
10004 trw_layer_track_alloc_colors ( vtl );
10006 trw_layer_calculate_bounds_waypoints ( vtl );
10007 trw_layer_calculate_bounds_tracks ( vtl );
10009 // Apply treeview sort after loading all the tracks for this layer
10010 // (rather than sorted insert on each individual track additional)
10011 // and after subsequent changes to the properties as the specified order may have changed.
10012 // since the sorting of a treeview section is now very quick
10013 // NB sorting is also performed after every name change as well to maintain the list order
10014 trw_layer_sort_all ( vtl );
10017 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10019 return vtl->coord_mode;
10023 * Uniquify the whole layer
10024 * Also requires the layers panel as the names shown there need updating too
10025 * Returns whether the operation was successful or not
10027 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10029 if ( vtl && vlp ) {
10030 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10031 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10032 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10038 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10040 vik_coord_convert ( &(wp->coord), *dest_mode );
10043 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10045 vik_track_convert ( tr, *dest_mode );
10048 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10050 if ( vtl->coord_mode != dest_mode )
10052 vtl->coord_mode = dest_mode;
10053 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10054 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10055 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10059 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10061 vtl->menu_selection = selection;
10064 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10066 return (vtl->menu_selection);
10069 /* ----------- Downloading maps along tracks --------------- */
10071 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10073 /* TODO: calculating based on current size of viewport */
10074 const gdouble w_at_zoom_0_125 = 0.0013;
10075 const gdouble h_at_zoom_0_125 = 0.0011;
10076 gdouble zoom_factor = zoom_level/0.125;
10078 wh->lat = h_at_zoom_0_125 * zoom_factor;
10079 wh->lon = w_at_zoom_0_125 * zoom_factor;
10081 return 0; /* all OK */
10084 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10086 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10087 (dist->lat >= ABS(to->north_south - from->north_south)))
10090 VikCoord *coord = g_malloc(sizeof(VikCoord));
10091 coord->mode = VIK_COORD_LATLON;
10093 if (ABS(gradient) < 1) {
10094 if (from->east_west > to->east_west)
10095 coord->east_west = from->east_west - dist->lon;
10097 coord->east_west = from->east_west + dist->lon;
10098 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10100 if (from->north_south > to->north_south)
10101 coord->north_south = from->north_south - dist->lat;
10103 coord->north_south = from->north_south + dist->lat;
10104 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10110 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10112 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10113 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10115 VikCoord *next = from;
10117 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10119 list = g_list_prepend(list, next);
10125 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10127 typedef struct _Rect {
10132 #define GLRECT(iter) ((Rect *)((iter)->data))
10135 GList *rects_to_download = NULL;
10138 if (get_download_area_width(vvp, zoom_level, &wh))
10141 GList *iter = tr->trackpoints;
10145 gboolean new_map = TRUE;
10146 VikCoord *cur_coord, tl, br;
10149 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10151 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10152 rect = g_malloc(sizeof(Rect));
10155 rect->center = *cur_coord;
10156 rects_to_download = g_list_prepend(rects_to_download, rect);
10161 gboolean found = FALSE;
10162 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10163 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10174 GList *fillins = NULL;
10175 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10176 /* seems that ATM the function get_next_coord works only for LATLON */
10177 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10178 /* fill-ins for far apart points */
10179 GList *cur_rect, *next_rect;
10180 for (cur_rect = rects_to_download;
10181 (next_rect = cur_rect->next) != NULL;
10182 cur_rect = cur_rect->next) {
10183 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10184 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10185 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10189 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10192 GList *iter = fillins;
10194 cur_coord = (VikCoord *)(iter->data);
10195 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10196 rect = g_malloc(sizeof(Rect));
10199 rect->center = *cur_coord;
10200 rects_to_download = g_list_prepend(rects_to_download, rect);
10205 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10206 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10210 for (iter = fillins; iter; iter = iter->next)
10211 g_free(iter->data);
10212 g_list_free(fillins);
10214 if (rects_to_download) {
10215 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10216 g_free(rect_iter->data);
10217 g_list_free(rects_to_download);
10221 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10225 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10226 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10227 gint selected_zoom, default_zoom;
10229 VikTrwLayer *vtl = pass_along[0];
10230 VikLayersPanel *vlp = pass_along[1];
10232 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10233 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10235 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10239 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10241 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10242 int num_maps = g_list_length(vmls);
10245 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10249 // Convert from list of vmls to list of names. Allowing the user to select one of them
10250 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10251 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10253 gchar **np = map_names;
10254 VikMapsLayer **lp = map_layers;
10256 for (i = 0; i < num_maps; i++) {
10257 vml = (VikMapsLayer *)(vmls->data);
10259 *np++ = vik_maps_layer_get_map_label(vml);
10262 // Mark end of the array lists
10266 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10267 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10268 if (cur_zoom == zoom_vals[default_zoom])
10271 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10273 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10276 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10279 for (i = 0; i < num_maps; i++)
10280 g_free(map_names[i]);
10282 g_free(map_layers);
10288 /**** lowest waypoint number calculation ***/
10289 static gint highest_wp_number_name_to_number(const gchar *name) {
10290 if ( strlen(name) == 3 ) {
10291 int n = atoi(name);
10292 if ( n < 100 && name[0] != '0' )
10294 if ( n < 10 && name[0] != '0' )
10302 static void highest_wp_number_reset(VikTrwLayer *vtl)
10304 vtl->highest_wp_number = -1;
10307 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10309 /* if is bigger that top, add it */
10310 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10311 if ( new_wp_num > vtl->highest_wp_number )
10312 vtl->highest_wp_number = new_wp_num;
10315 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10317 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10318 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10319 if ( vtl->highest_wp_number == old_wp_num ) {
10321 vtl->highest_wp_number--;
10323 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10324 /* search down until we find something that *does* exist */
10326 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10327 vtl->highest_wp_number--;
10328 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10333 /* get lowest unused number */
10334 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10337 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10339 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10340 return g_strdup(buf);
10344 * trw_layer_create_track_list_both:
10346 * Create the latest list of tracks and routes
10348 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10350 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10351 GList *tracks = NULL;
10352 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10353 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10355 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10358 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] )
10360 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
10362 gchar *title = NULL;
10363 if ( GPOINTER_TO_INT(pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10364 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10366 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10368 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), pass_along[2], trw_layer_create_track_list, FALSE );
10372 static void trw_layer_track_list_dialog ( gpointer lav[2] )
10374 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10375 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10377 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10378 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10382 static void trw_layer_waypoint_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: Waypoint List"), VIK_LAYER(vtl)->name );
10388 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );