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 #ifdef VIK_CONFIG_GEOTAG
40 #include "viktrwlayer_geotag.h"
41 #include "geotag_exif.h"
43 #include "garminsymbols.h"
44 #include "thumbnails.h"
45 #include "background.h"
50 #include "geonamessearch.h"
51 #ifdef VIK_CONFIG_OPENSTREETMAP
52 #include "osm-traces.h"
55 #include "datasources.h"
56 #include "datasource_gps.h"
57 #include "vikexttool_datasources.h"
60 #include "vikrouting.h"
62 #include "icons/icons.h"
76 #include <gdk/gdkkeysyms.h>
78 #include <glib/gstdio.h>
79 #include <glib/gi18n.h>
81 #define VIK_TRW_LAYER_TRACK_GC 6
82 #define VIK_TRW_LAYER_TRACK_GCS 10
83 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
84 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
85 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
86 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
87 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
88 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
90 #define DRAWMODE_BY_TRACK 0
91 #define DRAWMODE_BY_SPEED 1
92 #define DRAWMODE_ALL_SAME_COLOR 2
93 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
94 // as we are (re)calculating the colour for every point
99 /* this is how it knows when you click if you are clicking close to a trackpoint. */
100 #define TRACKPOINT_SIZE_APPROX 5
101 #define WAYPOINT_SIZE_APPROX 5
103 #define MIN_STOP_LENGTH 15
104 #define MAX_STOP_LENGTH 86400
105 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
106 /* this is multiplied by user-inputted value from 1-100. */
108 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
110 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
112 FS_XX_SMALL = 0, // 'xx-small'
115 FS_MEDIUM, // DEFAULT
122 struct _VikTrwLayer {
125 GHashTable *tracks_iters;
127 GHashTable *routes_iters;
128 GHashTable *waypoints_iters;
129 GHashTable *waypoints;
130 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
131 gboolean tracks_visible, routes_visible, waypoints_visible;
132 LatLonBBox waypoints_bbox;
136 guint8 drawpoints_size;
137 guint8 drawelevation;
138 guint8 elevation_factor;
142 guint8 drawdirections;
143 guint8 drawdirections_size;
144 guint8 line_thickness;
145 guint8 bg_line_thickness;
146 vik_layer_sort_order_t track_sort_order;
150 gboolean wp_draw_symbols;
151 font_size_t wp_font_size;
153 vik_layer_sort_order_t wp_sort_order;
155 gdouble track_draw_speed_factor;
157 GdkGC *track_1color_gc;
158 GdkColor track_color;
159 GdkGC *current_track_gc;
160 // Separate GC for a track's potential new point as drawn via separate method
161 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
162 GdkGC *current_track_newpoint_gc;
163 GdkGC *track_bg_gc; GdkColor track_bg_color;
164 GdkGC *waypoint_gc; GdkColor waypoint_color;
165 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
166 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
168 GdkFont *waypoint_font;
169 VikTrack *current_track; // ATM shared between new tracks and new routes
170 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
171 gboolean draw_sync_done;
172 gboolean draw_sync_do;
174 VikCoordMode coord_mode;
176 /* wp editing tool */
177 VikWaypoint *current_wp;
178 gpointer current_wp_id;
180 gboolean waypoint_rightclick;
182 /* track editing tool */
184 VikTrack *current_tp_track;
185 gpointer current_tp_id;
186 VikTrwLayerTpwin *tpwin;
188 /* track editing tool -- more specifically, moving tps */
191 /* route finder tool */
192 gboolean route_finder_started;
193 VikCoord route_finder_coord;
194 gboolean route_finder_check_added_track;
195 VikTrack *route_finder_added_track;
196 VikTrack *route_finder_current_track;
197 gboolean route_finder_append;
204 guint16 image_cache_size;
206 /* for waypoint text */
207 PangoLayout *wplabellayout;
209 gboolean has_verified_thumbnails;
211 GtkMenu *wp_right_click_menu;
212 GtkMenu *track_right_click_menu;
215 VikStdLayerMenuItem menu_selection;
217 gint highest_wp_number;
220 GtkWidget *tracks_analysis_dialog;
223 /* A caached waypoint image. */
226 gchar *image; /* filename */
229 struct DrawingParams {
234 guint16 width, height;
235 gdouble cc; // Cosine factor in track directions
236 gdouble ss; // Sine factor in track directions
237 const VikCoord *center;
238 gboolean one_zone, lat_lon;
239 gdouble ce1, ce2, cn1, cn2;
243 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
245 static void trw_layer_delete_item ( gpointer pass_along[6] );
246 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
247 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
249 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
250 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
252 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
253 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
255 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
256 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
258 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl );
260 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
261 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
262 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
263 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
264 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
265 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
266 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
267 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
268 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
269 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
270 static void trw_layer_append_track ( gpointer pass_along[6] );
271 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
272 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
273 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
274 static void trw_layer_split_segments ( gpointer pass_along[6] );
275 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
276 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
277 static void trw_layer_reverse ( gpointer pass_along[6] );
278 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
279 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
280 static void trw_layer_show_picture ( gpointer pass_along[6] );
281 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
283 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
284 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
285 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
286 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
287 static void trw_layer_new_wp ( gpointer lav[2] );
288 static void trw_layer_new_track ( gpointer lav[2] );
289 static void trw_layer_new_route ( gpointer lav[2] );
290 static void trw_layer_finish_track ( gpointer lav[2] );
291 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
292 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
293 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
294 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
295 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
296 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
297 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
298 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
299 #ifdef VIK_CONFIG_GEOTAG
300 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
301 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
302 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
303 static void trw_layer_geotagging ( gpointer lav[2] );
305 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
306 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
307 #ifdef VIK_CONFIG_OPENSTREETMAP
308 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
309 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
311 #ifdef VIK_CONFIG_GEOCACHES
312 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
314 #ifdef VIK_CONFIG_GEOTAG
315 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
317 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
318 static void trw_layer_gps_upload ( gpointer lav[2] );
320 // Specific route versions:
321 // Most track handling functions can handle operating on the route list
322 // However these ones are easier in separate functions
323 static void trw_layer_auto_routes_view ( gpointer lav[2] );
324 static void trw_layer_delete_all_routes ( gpointer lav[2] );
325 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
328 static void trw_layer_properties_item ( gpointer pass_along[7] );
329 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
330 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
331 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
333 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
334 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
335 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
337 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
338 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
339 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
340 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
342 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
343 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
344 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
345 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
346 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
347 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
348 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
349 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
350 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
351 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
352 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
353 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
354 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
355 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
356 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
357 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
358 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
359 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
360 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
361 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
362 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
363 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
364 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
366 static void cached_pixbuf_free ( CachedPixbuf *cp );
367 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
369 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
370 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
372 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
373 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
375 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
376 static void highest_wp_number_reset(VikTrwLayer *vtl);
377 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
378 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
380 // Note for the following tool GtkRadioActionEntry texts:
381 // the very first text value is an internal name not displayed anywhere
382 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
383 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
384 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
385 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
386 static VikToolInterface trw_layer_tools[] = {
387 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
388 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
389 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
391 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
393 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
394 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
395 (VikToolMouseFunc) tool_new_track_click,
396 (VikToolMouseMoveFunc) tool_new_track_move,
397 (VikToolMouseFunc) tool_new_track_release,
398 (VikToolKeyFunc) tool_new_track_key_press,
399 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
400 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
402 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
403 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
404 (VikToolMouseFunc) tool_new_route_click,
405 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
406 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
407 (VikToolKeyFunc) tool_new_track_key_press, // -/#
408 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
409 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
411 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
412 (VikToolConstructorFunc) tool_edit_waypoint_create,
413 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
415 (VikToolMouseFunc) tool_edit_waypoint_click,
416 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
417 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
419 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
421 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
422 (VikToolConstructorFunc) tool_edit_trackpoint_create,
423 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
425 (VikToolMouseFunc) tool_edit_trackpoint_click,
426 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
427 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
429 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
431 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
432 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
433 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
435 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
437 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
438 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
439 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
441 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
445 TOOL_CREATE_WAYPOINT=0,
449 TOOL_EDIT_TRACKPOINT,
455 /****** PARAMETERS ******/
457 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
458 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
460 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
461 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
463 #define MIN_POINT_SIZE 2
464 #define MAX_POINT_SIZE 10
466 #define MIN_ARROW_SIZE 3
467 #define MAX_ARROW_SIZE 20
469 static VikLayerParamScale params_scales[] = {
470 /* min max step digits */
471 { 1, 10, 1, 0 }, /* line_thickness */
472 { 0, 100, 1, 0 }, /* track draw speed factor */
473 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
474 /* 5 * step == how much to turn */
475 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
476 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
477 { 5, 500, 5, 0 }, // 5: image cache_size - " "
478 { 0, 8, 1, 0 }, // 6: Background line thickness
479 { 1, 64, 1, 0 }, /* wpsize */
480 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
481 { 1, 100, 1, 0 }, // 9: elevation factor
482 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
483 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
486 static gchar* params_font_sizes[] = {
487 N_("Extra Extra Small"),
493 N_("Extra Extra Large"),
496 // Needs to align with vik_layer_sort_order_t
497 static gchar* params_sort_order[] = {
499 N_("Name Ascending"),
500 N_("Name Descending"),
504 static VikLayerParamData black_color_default ( void ) {
505 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
507 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
508 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
509 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
510 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
511 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
512 static VikLayerParamData trackbgcolor_default ( void ) {
513 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
515 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
516 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
517 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
519 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
520 static VikLayerParamData wptextcolor_default ( void ) {
521 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
523 static VikLayerParamData wpbgcolor_default ( void ) {
524 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
526 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
527 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
529 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
530 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
531 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
533 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
535 VikLayerParam trw_layer_params[] = {
536 { 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 },
537 { 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 },
538 { 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 },
540 { 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 },
541 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
542 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
543 { 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 },
544 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
545 { 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 },
546 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
547 { 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 },
548 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
549 { 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 },
550 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
552 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
553 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 },
554 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
556 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
557 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
558 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
559 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
561 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
563 { 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 },
564 { 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 },
565 { 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 },
566 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
567 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
568 { 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 },
569 { 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 },
570 { 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 },
571 { 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 },
572 { 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 },
574 { 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 },
575 { 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 },
576 { 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 },
577 { 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 },
580 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
582 // Sublayer visibilities
623 *** 1) Add to trw_layer_params and enumeration
624 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
627 /****** END PARAMETERS ******/
629 /* Layer Interface function definitions */
630 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
631 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
632 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
633 static void trw_layer_free ( VikTrwLayer *trwlayer );
634 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
635 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
636 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
637 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
638 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
639 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
640 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
641 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
642 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
643 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
644 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
645 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
646 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
647 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
648 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
649 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
650 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
651 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
652 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
653 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
654 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
655 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
656 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
657 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
658 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
659 /* End Layer Interface function definitions */
661 VikLayerInterface vik_trw_layer_interface = {
668 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
672 params_groups, /* params_groups */
673 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
677 (VikLayerFuncCreate) trw_layer_create,
678 (VikLayerFuncRealize) trw_layer_realize,
679 (VikLayerFuncPostRead) trw_layer_post_read,
680 (VikLayerFuncFree) trw_layer_free,
682 (VikLayerFuncProperties) NULL,
683 (VikLayerFuncDraw) trw_layer_draw,
684 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
686 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
687 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
689 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
690 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
692 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
693 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
694 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
695 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
696 (VikLayerFuncLayerSelected) trw_layer_selected,
698 (VikLayerFuncMarshall) trw_layer_marshall,
699 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
701 (VikLayerFuncSetParam) trw_layer_set_param,
702 (VikLayerFuncGetParam) trw_layer_get_param,
704 (VikLayerFuncReadFileData) a_gpspoint_read_file,
705 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
707 (VikLayerFuncDeleteItem) trw_layer_del_item,
708 (VikLayerFuncCutItem) trw_layer_cut_item,
709 (VikLayerFuncCopyItem) trw_layer_copy_item,
710 (VikLayerFuncPasteItem) trw_layer_paste_item,
711 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
713 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
715 (VikLayerFuncSelectClick) trw_layer_select_click,
716 (VikLayerFuncSelectMove) trw_layer_select_move,
717 (VikLayerFuncSelectRelease) trw_layer_select_release,
718 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
721 GType vik_trw_layer_get_type ()
723 static GType vtl_type = 0;
727 static const GTypeInfo vtl_info =
729 sizeof (VikTrwLayerClass),
730 NULL, /* base_init */
731 NULL, /* base_finalize */
732 NULL, /* class init */
733 NULL, /* class_finalize */
734 NULL, /* class_data */
735 sizeof (VikTrwLayer),
737 NULL /* instance init */
739 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
745 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
747 static gpointer pass_along[6];
753 pass_along[1] = NULL;
754 pass_along[2] = GINT_TO_POINTER (subtype);
755 pass_along[3] = sublayer;
756 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
757 pass_along[5] = NULL;
759 trw_layer_delete_item ( pass_along );
762 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
764 static gpointer pass_along[6];
770 pass_along[1] = NULL;
771 pass_along[2] = GINT_TO_POINTER (subtype);
772 pass_along[3] = sublayer;
773 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
774 pass_along[5] = NULL;
776 trw_layer_copy_item_cb(pass_along);
777 trw_layer_cut_item_cb(pass_along);
780 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
782 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
783 gint subtype = GPOINTER_TO_INT (pass_along[2]);
784 gpointer * sublayer = pass_along[3];
788 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
792 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
793 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
794 if ( wp && wp->name )
797 name = NULL; // Broken :(
799 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
800 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
801 if ( trk && trk->name )
804 name = NULL; // Broken :(
807 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
808 if ( trk && trk->name )
811 name = NULL; // Broken :(
814 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
815 subtype, len, name, data);
819 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
821 trw_layer_copy_item_cb(pass_along);
822 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
823 trw_layer_delete_item(pass_along);
826 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
828 // Slightly cheating method, routing via the panels capability
829 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
832 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
842 GByteArray *ba = g_byte_array_new ();
844 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
845 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
846 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
847 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
849 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
852 g_byte_array_append ( ba, id, il );
860 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
867 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
871 w = vik_waypoint_unmarshall ( item, len );
872 // When copying - we'll create a new name based on the original
873 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
874 vik_trw_layer_add_waypoint ( vtl, name, w );
875 waypoint_convert (NULL, w, &vtl->coord_mode);
878 trw_layer_calculate_bounds_waypoints ( vtl );
880 // Consider if redraw necessary for the new item
881 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
882 vik_layer_emit_update ( VIK_LAYER(vtl) );
885 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
889 t = vik_track_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_TRACK, t->name);
892 vik_trw_layer_add_track ( vtl, name, t );
893 vik_track_convert (t, vtl->coord_mode);
896 // Consider if redraw necessary for the new item
897 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
898 vik_layer_emit_update ( VIK_LAYER(vtl) );
901 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
905 t = vik_track_unmarshall ( item, len );
906 // When copying - we'll create a new name based on the original
907 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
908 vik_trw_layer_add_route ( vtl, name, t );
909 vik_track_convert (t, vtl->coord_mode);
912 // Consider if redraw necessary for the new item
913 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
914 vik_layer_emit_update ( VIK_LAYER(vtl) );
920 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
927 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
931 case PARAM_TV: vtl->tracks_visible = data.b; break;
932 case PARAM_WV: vtl->waypoints_visible = data.b; break;
933 case PARAM_RV: vtl->routes_visible = data.b; break;
934 case PARAM_DM: vtl->drawmode = data.u; break;
936 vtl->track_color = data.c;
937 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
939 case PARAM_DP: vtl->drawpoints = data.b; break;
941 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
942 vtl->drawpoints_size = data.u;
944 case PARAM_DE: vtl->drawelevation = data.b; break;
945 case PARAM_DS: vtl->drawstops = data.b; break;
946 case PARAM_DL: vtl->drawlines = data.b; break;
947 case PARAM_DD: vtl->drawdirections = data.b; break;
949 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
950 vtl->drawdirections_size = data.u;
952 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
953 vtl->stop_length = data.u;
955 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
956 vtl->elevation_factor = data.u;
958 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
960 vtl->line_thickness = data.u;
961 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
964 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
966 vtl->bg_line_thickness = data.u;
967 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
971 vtl->track_bg_color = data.c;
972 if ( vtl->track_bg_gc )
973 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
975 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
976 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
977 case PARAM_DLA: vtl->drawlabels = data.b; break;
978 case PARAM_DI: vtl->drawimages = data.b; break;
979 case PARAM_IS: if ( data.u != vtl->image_size )
981 vtl->image_size = data.u;
982 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
983 g_queue_free ( vtl->image_cache );
984 vtl->image_cache = g_queue_new ();
987 case PARAM_IA: vtl->image_alpha = data.u; break;
988 case PARAM_ICS: vtl->image_cache_size = data.u;
989 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
990 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
993 vtl->waypoint_color = data.c;
994 if ( vtl->waypoint_gc )
995 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
998 vtl->waypoint_text_color = data.c;
999 if ( vtl->waypoint_text_gc )
1000 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1003 vtl->waypoint_bg_color = data.c;
1004 if ( vtl->waypoint_bg_gc )
1005 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1008 vtl->wpbgand = data.b;
1009 if ( vtl->waypoint_bg_gc )
1010 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1012 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1013 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1014 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1015 case PARAM_WPFONTSIZE:
1016 if ( data.u < FS_NUM_SIZES ) {
1017 vtl->wp_font_size = data.u;
1018 g_free ( vtl->wp_fsize_str );
1019 switch ( vtl->wp_font_size ) {
1020 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1021 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1022 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1023 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1024 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1025 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1026 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1030 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1035 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1037 VikLayerParamData rv;
1040 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1041 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1042 case PARAM_RV: rv.b = vtl->routes_visible; break;
1043 case PARAM_DM: rv.u = vtl->drawmode; break;
1044 case PARAM_TC: rv.c = vtl->track_color; break;
1045 case PARAM_DP: rv.b = vtl->drawpoints; break;
1046 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1047 case PARAM_DE: rv.b = vtl->drawelevation; break;
1048 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1049 case PARAM_DS: rv.b = vtl->drawstops; break;
1050 case PARAM_SL: rv.u = vtl->stop_length; break;
1051 case PARAM_DL: rv.b = vtl->drawlines; break;
1052 case PARAM_DD: rv.b = vtl->drawdirections; break;
1053 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1054 case PARAM_LT: rv.u = vtl->line_thickness; break;
1055 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1056 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1057 case PARAM_DI: rv.b = vtl->drawimages; break;
1058 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1059 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1060 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1061 case PARAM_IS: rv.u = vtl->image_size; break;
1062 case PARAM_IA: rv.u = vtl->image_alpha; break;
1063 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1064 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1065 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1066 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1067 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1068 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1069 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1070 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1071 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1072 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1077 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1084 // Use byte arrays to store sublayer data
1085 // much like done elsewhere e.g. vik_layer_marshall_params()
1086 GByteArray *ba = g_byte_array_new ( );
1091 guint object_length;
1094 // the length of the item
1095 // the sublayer type of item
1096 // the the actual item
1097 #define tlm_append(object_pointer, size, type) \
1099 object_length = (size); \
1100 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1101 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1102 g_byte_array_append ( ba, (object_pointer), object_length );
1104 // Layer parameters first
1105 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1106 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1107 g_byte_array_append ( ba, pd, pl );
1110 // Now sublayer data
1111 GHashTableIter iter;
1112 gpointer key, value;
1115 g_hash_table_iter_init ( &iter, vtl->waypoints );
1116 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1117 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1118 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1123 g_hash_table_iter_init ( &iter, vtl->tracks );
1124 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1125 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1126 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1131 g_hash_table_iter_init ( &iter, vtl->routes );
1132 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1133 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1134 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1144 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1146 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1148 gint consumed_length;
1150 // First the overall layer parameters
1151 memcpy(&pl, data, sizeof(pl));
1153 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1156 consumed_length = pl;
1157 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1159 #define tlm_size (*(gint *)data)
1160 // See marshalling above for order of how this is written
1162 data += sizeof_len_and_subtype + tlm_size;
1164 // Now the individual sublayers:
1166 while ( *data && consumed_length < len ) {
1167 // Normally four extra bytes at the end of the datastream
1168 // (since it's a GByteArray and that's where it's length is stored)
1169 // So only attempt read when there's an actual block of sublayer data
1170 if ( consumed_length + tlm_size < len ) {
1172 // Reuse pl to read the subtype from the data stream
1173 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1175 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1176 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1177 gchar *name = g_strdup ( trk->name );
1178 vik_trw_layer_add_track ( vtl, name, trk );
1181 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1182 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1183 gchar *name = g_strdup ( wp->name );
1184 vik_trw_layer_add_waypoint ( vtl, name, wp );
1187 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1188 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1189 gchar *name = g_strdup ( trk->name );
1190 vik_trw_layer_add_route ( vtl, name, trk );
1194 consumed_length += tlm_size + sizeof_len_and_subtype;
1197 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1199 // Not stored anywhere else so need to regenerate
1200 trw_layer_calculate_bounds_waypoints ( vtl );
1205 // Keep interesting hash function at least visible
1207 static guint strcase_hash(gconstpointer v)
1209 // 31 bit hash function
1212 gchar s[128]; // malloc is too slow for reading big files
1215 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1216 p[i] = toupper(t[i]);
1222 for (p += 1; *p != '\0'; p++)
1223 h = (h << 5) - h + *p;
1230 // Stick a 1 at the end of the function name to make it more unique
1231 // thus more easily searchable in a simple text editor
1232 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1234 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1235 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1237 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1238 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1240 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1241 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1242 // and with normal PC processing capabilities - it has negligibile performance impact
1243 // This also minimized the amount of rework - as the management of the hash tables already exists.
1245 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1246 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1247 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1249 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1250 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1251 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1252 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1253 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1254 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1256 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1258 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1260 // Param settings that are not available via the GUI
1261 // Force to on after processing params (which defaults them to off with a zero value)
1262 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1264 rv->draw_sync_done = TRUE;
1265 rv->draw_sync_do = TRUE;
1266 // Everything else is 0, FALSE or NULL
1272 static void trw_layer_free ( VikTrwLayer *trwlayer )
1274 g_hash_table_destroy(trwlayer->waypoints);
1275 g_hash_table_destroy(trwlayer->waypoints_iters);
1276 g_hash_table_destroy(trwlayer->tracks);
1277 g_hash_table_destroy(trwlayer->tracks_iters);
1278 g_hash_table_destroy(trwlayer->routes);
1279 g_hash_table_destroy(trwlayer->routes_iters);
1281 /* ODC: replace with GArray */
1282 trw_layer_free_track_gcs ( trwlayer );
1284 if ( trwlayer->wp_right_click_menu )
1285 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1287 if ( trwlayer->track_right_click_menu )
1288 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1290 if ( trwlayer->wplabellayout != NULL)
1291 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1293 if ( trwlayer->waypoint_gc != NULL )
1294 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1296 if ( trwlayer->waypoint_text_gc != NULL )
1297 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1299 if ( trwlayer->waypoint_bg_gc != NULL )
1300 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1302 g_free ( trwlayer->wp_fsize_str );
1304 if ( trwlayer->tpwin != NULL )
1305 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1307 if ( trwlayer->tracks_analysis_dialog != NULL )
1308 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1310 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1311 g_queue_free ( trwlayer->image_cache );
1314 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1318 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1319 dp->xmpp = vik_viewport_get_xmpp ( vp );
1320 dp->ympp = vik_viewport_get_ympp ( vp );
1321 dp->width = vik_viewport_get_width ( vp );
1322 dp->height = vik_viewport_get_height ( vp );
1323 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1324 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1326 dp->center = vik_viewport_get_center ( vp );
1327 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1328 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1333 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1334 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1335 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1337 dp->ce1 = dp->center->east_west-w2;
1338 dp->ce2 = dp->center->east_west+w2;
1339 dp->cn1 = dp->center->north_south-h2;
1340 dp->cn2 = dp->center->north_south+h2;
1341 } else if ( dp->lat_lon ) {
1342 VikCoord upperleft, bottomright;
1343 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1344 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1345 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1346 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1347 dp->ce1 = upperleft.east_west;
1348 dp->ce2 = bottomright.east_west;
1349 dp->cn1 = bottomright.north_south;
1350 dp->cn2 = upperleft.north_south;
1353 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1357 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1358 * Here a simple traffic like light colour system is used:
1359 * . slow points are red
1360 * . average is yellow
1361 * . fast points are green
1363 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1366 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1367 if ( average_speed > 0 ) {
1368 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1369 if ( rv < low_speed )
1370 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1371 else if ( rv > high_speed )
1372 return VIK_TRW_LAYER_TRACK_GC_FAST;
1374 return VIK_TRW_LAYER_TRACK_GC_AVER;
1377 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1380 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1382 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1383 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1384 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1385 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1388 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1390 if ( ! track->visible )
1393 /* TODO: this function is a mess, get rid of any redundancy */
1394 GList *list = track->trackpoints;
1396 gboolean useoldvals = TRUE;
1398 gboolean drawpoints;
1400 gboolean drawelevation;
1401 gdouble min_alt, max_alt, alt_diff = 0;
1403 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1404 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1407 if ( dp->vtl->drawelevation )
1409 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1410 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1411 alt_diff = max_alt - min_alt;
1414 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1415 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1416 trw_layer_draw_track ( id, track, dp, TRUE );
1418 if ( draw_track_outline )
1419 drawpoints = drawstops = FALSE;
1421 drawpoints = dp->vtl->drawpoints;
1422 drawstops = dp->vtl->drawstops;
1425 gboolean drawing_highlight = FALSE;
1426 /* Current track - used for creation */
1427 if ( track == dp->vtl->current_track )
1428 main_gc = dp->vtl->current_track_gc;
1430 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1431 /* Draw all tracks of the layer in special colour */
1432 /* if track is member of selected layer or is the current selected track
1433 then draw in the highlight colour.
1434 NB this supercedes the drawmode */
1435 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1436 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1437 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1438 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1439 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1440 drawing_highlight = TRUE;
1443 if ( !drawing_highlight ) {
1444 // Still need to figure out the gc according to the drawing mode:
1445 switch ( dp->vtl->drawmode ) {
1446 case DRAWMODE_BY_TRACK:
1447 if ( dp->vtl->track_1color_gc )
1448 g_object_unref ( dp->vtl->track_1color_gc );
1449 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1450 main_gc = dp->vtl->track_1color_gc;
1453 // Mostly for DRAWMODE_ALL_SAME_COLOR
1454 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1455 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1462 int x, y, oldx, oldy;
1463 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1465 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1467 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1469 // Draw the first point as something a bit different from the normal points
1470 // ATM it's slightly bigger and a triangle
1472 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1473 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1479 gdouble average_speed = 0.0;
1480 gdouble low_speed = 0.0;
1481 gdouble high_speed = 0.0;
1482 // If necessary calculate these values - which is done only once per track redraw
1483 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1484 // the percentage factor away from the average speed determines transistions between the levels
1485 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1486 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1487 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1490 while ((list = g_list_next(list)))
1492 tp = VIK_TRACKPOINT(list->data);
1493 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1495 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1496 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1497 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1498 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1499 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1501 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1504 * If points are the same in display coordinates, don't draw.
1506 if ( useoldvals && x == oldx && y == oldy )
1508 // Still need to process points to ensure 'stops' are drawn if required
1509 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1510 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1511 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 );
1516 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1517 if ( drawpoints || dp->vtl->drawlines ) {
1518 // setup main_gc for both point and line drawing
1519 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1520 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 ) );
1524 if ( drawpoints && ! draw_track_outline )
1529 * The concept of drawing stops is that a trackpoint
1530 * that is if the next trackpoint has a timestamp far into
1531 * the future, we draw a circle of 6x trackpoint size,
1532 * instead of a rectangle of 2x trackpoint size.
1533 * This is drawn first so the trackpoint will be drawn on top
1536 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1537 /* Stop point. Draw 6x circle. Always in redish colour */
1538 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 );
1540 /* Regular point - draw 2x square. */
1541 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1544 /* Final point - draw 4x circle. */
1545 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 );
1548 if ((!tp->newsegment) && (dp->vtl->drawlines))
1551 /* UTM only: zone check */
1552 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1553 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1556 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1558 if ( draw_track_outline ) {
1559 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1563 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1565 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1567 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1572 tmp[1].y = oldy-FIXALTITUDE(list->data);
1574 tmp[2].y = y-FIXALTITUDE(list->next->data);
1579 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1580 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1582 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1583 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1585 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1590 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1591 // Draw an arrow at the mid point to show the direction of the track
1592 // Code is a rework from vikwindow::draw_ruler()
1593 gint midx = (oldx + x) / 2;
1594 gint midy = (oldy + y) / 2;
1596 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1597 // Avoid divide by zero and ensure at least 1 pixel big
1599 gdouble dx = (oldx - midx) / len;
1600 gdouble dy = (oldy - midy) / len;
1601 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1602 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1612 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1614 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1615 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1617 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1619 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1620 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 ));
1624 * If points are the same in display coordinates, don't draw.
1626 if ( x != oldx || y != oldy )
1628 if ( draw_track_outline )
1629 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1631 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1637 * If points are the same in display coordinates, don't draw.
1639 if ( x != oldx && y != oldy )
1641 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1642 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1652 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1654 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1655 trw_layer_draw_track ( id, track, dp, FALSE );
1659 static void cached_pixbuf_free ( CachedPixbuf *cp )
1661 g_object_unref ( G_OBJECT(cp->pixbuf) );
1662 g_free ( cp->image );
1665 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1667 return strcmp ( cp->image, name );
1670 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1673 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1674 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1675 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1678 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1680 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1682 if ( wp->image && dp->vtl->drawimages )
1684 GdkPixbuf *pixbuf = NULL;
1687 if ( dp->vtl->image_alpha == 0)
1690 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1692 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1695 gchar *image = wp->image;
1696 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1697 if ( ! regularthumb )
1699 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1700 image = "\x12\x00"; /* this shouldn't occur naturally. */
1704 CachedPixbuf *cp = NULL;
1705 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1706 if ( dp->vtl->image_size == 128 )
1707 cp->pixbuf = regularthumb;
1710 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1711 g_assert ( cp->pixbuf );
1712 g_object_unref ( G_OBJECT(regularthumb) );
1714 cp->image = g_strdup ( image );
1716 /* needed so 'click picture' tool knows how big the pic is; we don't
1717 * store it in cp because they may have been freed already. */
1718 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1719 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1721 g_queue_push_head ( dp->vtl->image_cache, cp );
1722 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1723 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1725 pixbuf = cp->pixbuf;
1729 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1735 w = gdk_pixbuf_get_width ( pixbuf );
1736 h = gdk_pixbuf_get_height ( pixbuf );
1738 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1740 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1741 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1742 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1743 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
1744 // Highlighted - so draw a little border around the chosen one
1745 // single line seems a little weak so draw 2 of them
1746 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1747 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1748 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1749 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1752 if ( dp->vtl->image_alpha == 255 )
1753 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1755 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1757 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1761 // Draw appropriate symbol - either symbol image or simple types
1762 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
1763 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 );
1765 else if ( wp == dp->vtl->current_wp ) {
1766 switch ( dp->vtl->wp_symbol ) {
1767 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;
1768 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;
1769 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;
1770 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 );
1771 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 );
1775 switch ( dp->vtl->wp_symbol ) {
1776 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;
1777 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;
1778 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;
1779 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 );
1780 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;
1784 if ( dp->vtl->drawlabels )
1786 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1787 gint label_x, label_y;
1789 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1791 // Could this stored in the waypoint rather than recreating each pass?
1792 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
1794 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1795 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1797 // Fallback if parse failure
1798 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1800 g_free ( wp_label_markup );
1802 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1803 label_x = x - width/2;
1804 if ( wp->symbol_pixbuf )
1805 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
1807 label_y = y - dp->vtl->wp_size - height - 2;
1809 /* if highlight mode on, then draw background text in highlight colour */
1810 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1811 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
1812 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
1813 wp == vik_window_get_selected_waypoint ( dp->vw ) )
1814 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1816 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1819 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1821 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1826 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1828 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
1829 trw_layer_draw_waypoint ( id, wp, dp );
1833 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1835 static struct DrawingParams dp;
1836 g_assert ( l != NULL );
1838 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1840 if ( l->tracks_visible )
1841 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1843 if ( l->routes_visible )
1844 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1846 if (l->waypoints_visible)
1847 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
1850 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1853 if ( vtl->track_bg_gc )
1855 g_object_unref ( vtl->track_bg_gc );
1856 vtl->track_bg_gc = NULL;
1858 if ( vtl->track_1color_gc )
1860 g_object_unref ( vtl->track_1color_gc );
1861 vtl->track_1color_gc = NULL;
1863 if ( vtl->current_track_gc )
1865 g_object_unref ( vtl->current_track_gc );
1866 vtl->current_track_gc = NULL;
1868 if ( vtl->current_track_newpoint_gc )
1870 g_object_unref ( vtl->current_track_newpoint_gc );
1871 vtl->current_track_newpoint_gc = NULL;
1874 if ( ! vtl->track_gc )
1876 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1877 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1878 g_array_free ( vtl->track_gc, TRUE );
1879 vtl->track_gc = NULL;
1882 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1884 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1885 gint width = vtl->line_thickness;
1887 if ( vtl->track_gc )
1888 trw_layer_free_track_gcs ( vtl );
1890 if ( vtl->track_bg_gc )
1891 g_object_unref ( vtl->track_bg_gc );
1892 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
1894 // Ensure new track drawing heeds line thickness setting
1895 // however always have a minium of 2, as 1 pixel is really narrow
1896 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1898 if ( vtl->current_track_gc )
1899 g_object_unref ( vtl->current_track_gc );
1900 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1901 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1903 // 'newpoint' gc is exactly the same as the current track gc
1904 if ( vtl->current_track_newpoint_gc )
1905 g_object_unref ( vtl->current_track_newpoint_gc );
1906 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1907 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1909 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1911 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1912 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1914 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1915 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1916 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1918 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
1920 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1923 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1925 VikTrwLayer *rv = trw_layer_new1 ( vp );
1926 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1928 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
1929 /* early exit, as the rest is GUI related */
1933 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1934 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
1936 trw_layer_new_track_gcs ( rv, vp );
1938 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
1939 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
1940 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
1941 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
1943 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1945 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1950 #define SMALL_ICON_SIZE 18
1952 * Can accept a null symbol, and may return null value
1954 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1956 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1957 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1958 // So needing a small icon for the treeview may need some resizing:
1959 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1960 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1964 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1966 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1968 GdkPixbuf *pixbuf = NULL;
1970 if ( track->has_color ) {
1971 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1972 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1973 // Here is some magic found to do the conversion
1974 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1975 guint32 pixel = ((track->color.red & 0xff00) << 16) |
1976 ((track->color.green & 0xff00) << 8) |
1977 (track->color.blue & 0xff00);
1979 gdk_pixbuf_fill ( pixbuf, pixel );
1982 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 );
1985 g_object_unref (pixbuf);
1987 *new_iter = *((GtkTreeIter *) pass_along[1]);
1988 if ( track->is_route )
1989 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1991 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1993 if ( ! track->visible )
1994 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1997 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1999 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2001 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 );
2003 *new_iter = *((GtkTreeIter *) pass_along[1]);
2004 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2006 if ( ! wp->visible )
2007 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2010 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2012 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2015 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2017 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2020 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2022 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2025 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2028 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2030 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2031 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2033 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2035 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2038 if ( g_hash_table_size (vtl->routes) > 0 ) {
2039 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2041 pass_along[0] = &(vtl->routes_iter);
2042 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2044 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2046 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2049 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2050 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2052 pass_along[0] = &(vtl->waypoints_iter);
2053 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2055 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2057 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2062 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2066 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2067 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2068 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2069 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2071 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2073 return (t->visible ^= 1);
2077 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2079 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2081 return (t->visible ^= 1);
2085 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2087 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2089 return (t->visible ^= 1);
2098 * Return a property about tracks for this layer
2100 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2102 return vtl->line_thickness;
2105 // Structure to hold multiple track information for a layer
2114 * Build up layer multiple track information via updating the tooltip_tracks structure
2116 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2118 tt->length = tt->length + vik_track_get_length (tr);
2120 // Ensure times are available
2121 if ( tr->trackpoints &&
2122 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2123 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2126 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2127 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2129 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2130 // Hence initialize to the first 'proper' value
2131 if ( tt->start_time == 0 )
2132 tt->start_time = t1;
2133 if ( tt->end_time == 0 )
2136 // Update find the earliest / last times
2137 if ( t1 < tt->start_time )
2138 tt->start_time = t1;
2139 if ( t2 > tt->end_time )
2142 // Keep track of total time
2143 // there maybe gaps within a track (eg segments)
2144 // but this should be generally good enough for a simple indicator
2145 tt->duration = tt->duration + (int)(t2-t1);
2150 * Generate tooltip text for the layer.
2151 * This is relatively complicated as it considers information for
2152 * no tracks, a single track or multiple tracks
2153 * (which may or may not have timing information)
2155 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2166 static gchar tmp_buf[128];
2169 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2171 // Safety check - I think these should always be valid
2172 if ( vtl->tracks && vtl->waypoints ) {
2173 tooltip_tracks tt = { 0.0, 0, 0 };
2174 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2176 GDate* gdate_start = g_date_new ();
2177 g_date_set_time_t (gdate_start, tt.start_time);
2179 GDate* gdate_end = g_date_new ();
2180 g_date_set_time_t (gdate_end, tt.end_time);
2182 if ( g_date_compare (gdate_start, gdate_end) ) {
2183 // Dates differ so print range on separate line
2184 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2185 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2186 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2189 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2190 if ( tt.start_time != 0 )
2191 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2195 if ( tt.length > 0.0 ) {
2196 gdouble len_in_units;
2198 // Setup info dependent on distance units
2199 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2200 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2201 len_in_units = VIK_METERS_TO_MILES(tt.length);
2204 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2205 len_in_units = tt.length/1000.0;
2208 // Timing information if available
2210 if ( tt.duration > 0 ) {
2211 g_snprintf (tbuf1, sizeof(tbuf1),
2212 _(" in %d:%02d hrs:mins"),
2213 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2215 g_snprintf (tbuf2, sizeof(tbuf2),
2216 _("\n%sTotal Length %.1f %s%s"),
2217 tbuf3, len_in_units, tbuf4, tbuf1);
2220 // Put together all the elements to form compact tooltip text
2221 g_snprintf (tmp_buf, sizeof(tmp_buf),
2222 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2223 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2225 g_date_free (gdate_start);
2226 g_date_free (gdate_end);
2233 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2237 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2239 // Very simple tooltip - may expand detail in the future...
2240 static gchar tmp_buf[32];
2241 g_snprintf (tmp_buf, sizeof(tmp_buf),
2243 g_hash_table_size (l->tracks));
2247 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2249 // Very simple tooltip - may expand detail in the future...
2250 static gchar tmp_buf[32];
2251 g_snprintf (tmp_buf, sizeof(tmp_buf),
2253 g_hash_table_size (l->routes));
2258 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2259 // Same tooltip for a route
2260 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2263 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2264 tr = g_hash_table_lookup ( l->tracks, sublayer );
2266 tr = g_hash_table_lookup ( l->routes, sublayer );
2269 // Could be a better way of handling strings - but this works...
2270 gchar time_buf1[20];
2271 gchar time_buf2[20];
2272 time_buf1[0] = '\0';
2273 time_buf2[0] = '\0';
2274 static gchar tmp_buf[100];
2275 // Compact info: Short date eg (11/20/99), duration and length
2276 // Hopefully these are the things that are most useful and so promoted into the tooltip
2277 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2278 // %x The preferred date representation for the current locale without the time.
2279 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2280 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2281 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2283 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2286 // Get length and consider the appropriate distance units
2287 gdouble tr_len = vik_track_get_length(tr);
2288 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2289 switch (dist_units) {
2290 case VIK_UNITS_DISTANCE_KILOMETRES:
2291 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2293 case VIK_UNITS_DISTANCE_MILES:
2294 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2303 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2305 // Very simple tooltip - may expand detail in the future...
2306 static gchar tmp_buf[32];
2307 g_snprintf (tmp_buf, sizeof(tmp_buf),
2309 g_hash_table_size (l->waypoints));
2313 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2315 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2316 // NB It's OK to return NULL
2321 return w->description;
2331 * Function to show basic track point information on the statusbar
2333 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2336 switch (a_vik_get_units_height ()) {
2337 case VIK_UNITS_HEIGHT_FEET:
2338 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2341 //VIK_UNITS_HEIGHT_METRES:
2342 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2347 if ( trkpt->has_timestamp ) {
2348 // Compact date time format
2349 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2353 // Position is put later on, as this bit may not be seen if the display is not big enough,
2354 // one can easily use the current pointer position to see this if needed
2355 gchar *lat = NULL, *lon = NULL;
2356 static struct LatLon ll;
2357 vik_coord_to_latlon (&(trkpt->coord), &ll);
2358 a_coords_latlon_to_string ( &ll, &lat, &lon );
2361 // Again is put later on, as this bit may not be seen if the display is not big enough
2362 // trackname can be seen from the treeview (when enabled)
2363 // Also name could be very long to not leave room for anything else
2366 if ( vtl->current_tp_track ) {
2367 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
2370 // Combine parts to make overall message
2371 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2372 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2379 * Function to show basic waypoint information on the statusbar
2381 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2384 switch (a_vik_get_units_height ()) {
2385 case VIK_UNITS_HEIGHT_FEET:
2386 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2389 //VIK_UNITS_HEIGHT_METRES:
2390 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2394 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2395 // one can easily use the current pointer position to see this if needed
2396 gchar *lat = NULL, *lon = NULL;
2397 static struct LatLon ll;
2398 vik_coord_to_latlon (&(wpt->coord), &ll);
2399 a_coords_latlon_to_string ( &ll, &lat, &lon );
2401 // Combine parts to make overall message
2404 // Add comment if available
2405 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2407 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2408 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2415 * General layer selection function, find out which bit is selected and take appropriate action
2417 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2420 l->current_wp = NULL;
2421 l->current_wp_id = NULL;
2422 trw_layer_cancel_current_tp ( l, FALSE );
2425 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2429 case VIK_TREEVIEW_TYPE_LAYER:
2431 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2432 /* Mark for redraw */
2437 case VIK_TREEVIEW_TYPE_SUBLAYER:
2441 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2443 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2444 /* Mark for redraw */
2448 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2450 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2451 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2452 /* Mark for redraw */
2456 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2458 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2459 /* Mark for redraw */
2463 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2465 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2466 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2467 /* Mark for redraw */
2471 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2473 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2474 /* Mark for redraw */
2478 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2480 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2482 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2483 // Show some waypoint info
2484 set_statusbar_msg_info_wpt ( l, wpt );
2485 /* Mark for redraw */
2492 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2501 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2506 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2511 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2516 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2518 return l->waypoints;
2521 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2523 return ! ( g_hash_table_size ( vtl->tracks ) ||
2524 g_hash_table_size ( vtl->routes ) ||
2525 g_hash_table_size ( vtl->waypoints ) );
2528 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2530 return vtl->tracks_visible;
2533 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2535 return vtl->routes_visible;
2538 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2540 return vtl->waypoints_visible;
2544 * ATM use a case sensitive find
2545 * Finds the first one
2547 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2549 if ( wp && wp->name )
2550 if ( ! strcmp ( wp->name, name ) )
2556 * Get waypoint by name - not guaranteed to be unique
2557 * Finds the first one
2559 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2561 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2565 * ATM use a case sensitive find
2566 * Finds the first one
2568 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2570 if ( trk && trk->name )
2571 if ( ! strcmp ( trk->name, name ) )
2577 * Get track by name - not guaranteed to be unique
2578 * Finds the first one
2580 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2582 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2586 * Get route by name - not guaranteed to be unique
2587 * Finds the first one
2589 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2591 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2594 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2596 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2597 maxmin[0].lat = trk->bbox.north;
2598 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2599 maxmin[1].lat = trk->bbox.south;
2600 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2601 maxmin[0].lon = trk->bbox.east;
2602 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2603 maxmin[1].lon = trk->bbox.west;
2606 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2608 // Continually reuse maxmin to find the latest maximum and minimum values
2609 // First set to waypoints bounds
2610 maxmin[0].lat = vtl->waypoints_bbox.north;
2611 maxmin[1].lat = vtl->waypoints_bbox.south;
2612 maxmin[0].lon = vtl->waypoints_bbox.east;
2613 maxmin[1].lon = vtl->waypoints_bbox.west;
2614 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2615 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2618 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2620 /* 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... */
2621 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2622 trw_layer_find_maxmin (vtl, maxmin);
2623 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2627 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2628 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2633 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2636 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2637 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2639 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2642 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2644 /* First set the center [in case previously viewing from elsewhere] */
2645 /* Then loop through zoom levels until provided positions are in view */
2646 /* This method is not particularly fast - but should work well enough */
2647 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2649 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2650 vik_viewport_set_center_coord ( vvp, &coord );
2652 /* Convert into definite 'smallest' and 'largest' positions */
2653 struct LatLon minmin;
2654 if ( maxmin[0].lat < maxmin[1].lat )
2655 minmin.lat = maxmin[0].lat;
2657 minmin.lat = maxmin[1].lat;
2659 struct LatLon maxmax;
2660 if ( maxmin[0].lon > maxmin[1].lon )
2661 maxmax.lon = maxmin[0].lon;
2663 maxmax.lon = maxmin[1].lon;
2665 /* Never zoom in too far - generally not that useful, as too close ! */
2666 /* Always recalculate the 'best' zoom level */
2668 vik_viewport_set_zoom ( vvp, zoom );
2670 gdouble min_lat, max_lat, min_lon, max_lon;
2671 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2672 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2673 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2674 /* NB I think the logic used in this test to determine if the bounds is within view
2675 fails if track goes across 180 degrees longitude.
2676 Hopefully that situation is not too common...
2677 Mind you viking doesn't really do edge locations to well anyway */
2678 if ( min_lat < minmin.lat &&
2679 max_lat > minmin.lat &&
2680 min_lon < maxmax.lon &&
2681 max_lon > maxmax.lon )
2682 /* Found within zoom level */
2687 vik_viewport_set_zoom ( vvp, zoom );
2691 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2693 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2694 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2695 trw_layer_find_maxmin (vtl, maxmin);
2696 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2699 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2704 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2706 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])) ) ) {
2707 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2710 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2713 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2715 GtkWidget *file_selector;
2717 gboolean failed = FALSE;
2718 file_selector = gtk_file_chooser_dialog_new (title,
2720 GTK_FILE_CHOOSER_ACTION_SAVE,
2721 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2722 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2724 gchar *cwd = g_get_current_dir();
2726 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
2730 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2732 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2734 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2735 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2737 gtk_widget_hide ( file_selector );
2738 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2739 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2740 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2745 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2747 gtk_widget_hide ( file_selector );
2748 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2749 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2750 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2755 gtk_widget_destroy ( file_selector );
2757 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2760 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2762 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2765 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2767 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2770 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2772 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2773 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2774 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2775 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2777 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2779 g_free ( auto_save_name );
2782 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2784 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2785 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2786 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2787 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2789 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2791 g_free ( auto_save_name );
2795 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2798 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2800 gchar *name_used = NULL;
2803 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2804 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2805 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2806 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
2808 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2812 gchar *quoted_file = g_shell_quote ( name_used );
2813 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2814 g_free ( quoted_file );
2815 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2817 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2818 g_error_free ( err );
2822 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2823 //g_remove ( name_used );
2824 // Perhaps should be deleted when the program ends?
2825 // For now leave it to the user to delete it / use system temp cleanup methods.
2826 g_free ( name_used );
2830 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2832 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2835 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2837 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2840 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2842 gpointer layer_and_vlp[2];
2843 layer_and_vlp[0] = pass_along[0];
2844 layer_and_vlp[1] = pass_along[1];
2846 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2848 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2849 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2851 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2853 if ( !trk || !trk->name )
2856 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2857 gchar *auto_save_name = g_strdup ( trk->name );
2858 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2859 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2861 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2863 g_free ( auto_save_name );
2867 VikWaypoint *wp; // input
2868 gpointer uuid; // output
2871 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2873 wpu_udata *user_data = udata;
2874 if ( wp == user_data->wp ) {
2875 user_data->uuid = id;
2881 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2883 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2884 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2885 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2887 GTK_RESPONSE_REJECT,
2889 GTK_RESPONSE_ACCEPT,
2892 GtkWidget *label, *entry;
2893 label = gtk_label_new(_("Waypoint Name:"));
2894 entry = gtk_entry_new();
2896 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
2897 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
2898 gtk_widget_show_all ( label );
2899 gtk_widget_show_all ( entry );
2901 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2903 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2905 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2906 // Find *first* wp with the given name
2907 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2910 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2913 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2914 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2916 // Find and select on the side panel
2921 // Hmmm, want key of it
2922 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2924 if ( wpf && udata.uuid ) {
2925 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2926 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2935 gtk_widget_destroy ( dia );
2938 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2940 gchar *default_name = highest_wp_number_get(vtl);
2941 VikWaypoint *wp = vik_waypoint_new();
2942 gchar *returned_name;
2944 wp->coord = *def_coord;
2946 // Attempt to auto set height if DEM data is available
2947 vik_waypoint_apply_dem_data ( wp, TRUE );
2949 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
2951 if ( returned_name )
2954 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2955 g_free (default_name);
2956 g_free (returned_name);
2959 g_free (default_name);
2960 vik_waypoint_free(wp);
2964 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2966 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2967 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2968 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2969 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2970 VikViewport *vvp = vik_window_viewport(vw);
2972 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2973 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2974 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2975 trw_layer_calculate_bounds_waypoints ( vtl );
2976 vik_layers_panel_emit_update ( vlp );
2979 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2981 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2982 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2983 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2985 trw_layer_find_maxmin (vtl, maxmin);
2986 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2987 trw_layer_calculate_bounds_waypoints ( vtl );
2988 vik_layers_panel_emit_update ( vlp );
2991 #ifdef VIK_CONFIG_GEOTAG
2992 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2994 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2996 // Update directly - not changing the mtime
2997 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3000 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3002 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3005 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3009 * Use code in separate file for this feature as reasonably complex
3011 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3013 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3014 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3015 // Unset so can be reverified later if necessary
3016 vtl->has_verified_thumbnails = FALSE;
3018 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3024 static void trw_layer_geotagging ( gpointer lav[2] )
3026 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3027 // Unset so can be reverified later if necessary
3028 vtl->has_verified_thumbnails = FALSE;
3030 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3037 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3040 * Acquire into this TRW Layer straight from GPS Device
3042 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3044 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3045 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3046 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3047 VikViewport *vvp = vik_window_viewport(vw);
3049 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3050 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3054 * Acquire into this TRW Layer from Directions
3056 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3058 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3059 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3060 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3061 VikViewport *vvp = vik_window_viewport(vw);
3063 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3066 #ifdef VIK_CONFIG_OPENSTREETMAP
3068 * Acquire into this TRW Layer from OSM
3070 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3072 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3073 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3074 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3075 VikViewport *vvp = vik_window_viewport(vw);
3077 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3081 * Acquire into this TRW Layer from OSM for 'My' Traces
3083 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3085 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3086 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3087 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3088 VikViewport *vvp = vik_window_viewport(vw);
3090 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3094 #ifdef VIK_CONFIG_GEOCACHES
3096 * Acquire into this TRW Layer from Geocaching.com
3098 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3100 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3101 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3102 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3103 VikViewport *vvp = vik_window_viewport(vw);
3105 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3109 #ifdef VIK_CONFIG_GEOTAG
3111 * Acquire into this TRW Layer from images
3113 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3115 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3116 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3117 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3118 VikViewport *vvp = vik_window_viewport(vw);
3120 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3121 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3123 // Reverify thumbnails as they may have changed
3124 vtl->has_verified_thumbnails = FALSE;
3125 trw_layer_verify_thumbnails ( vtl, NULL );
3129 static void trw_layer_gps_upload ( gpointer lav[2] )
3131 gpointer pass_along[6];
3132 pass_along[0] = lav[0];
3133 pass_along[1] = lav[1];
3134 pass_along[2] = NULL; // No track - operate on the layer
3135 pass_along[3] = NULL;
3136 pass_along[4] = NULL;
3137 pass_along[5] = NULL;
3139 trw_layer_gps_upload_any ( pass_along );
3143 * If pass_along[3] is defined that this will upload just that track
3145 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3147 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3148 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3150 // May not actually get a track here as pass_along[2&3] can be null
3151 VikTrack *track = NULL;
3152 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3153 gboolean xfer_all = FALSE;
3155 if ( pass_along[2] ) {
3157 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3158 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3161 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3162 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3165 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3168 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3172 else if ( !pass_along[4] )
3173 xfer_all = TRUE; // i.e. whole layer
3175 if (track && !track->visible) {
3176 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3180 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3181 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3182 GTK_DIALOG_DESTROY_WITH_PARENT,
3184 GTK_RESPONSE_ACCEPT,
3186 GTK_RESPONSE_REJECT,
3189 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3190 GtkWidget *response_w = NULL;
3191 #if GTK_CHECK_VERSION (2, 20, 0)
3192 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3196 gtk_widget_grab_focus ( response_w );
3198 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3200 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3201 datasource_gps_clean_up ( dgs );
3202 gtk_widget_destroy ( dialog );
3206 // Get info from reused datasource dialog widgets
3207 gchar* protocol = datasource_gps_get_protocol ( dgs );
3208 gchar* port = datasource_gps_get_descriptor ( dgs );
3209 // NB don't free the above strings as they're references to values held elsewhere
3210 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3211 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3212 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3213 gboolean turn_off = datasource_gps_get_off ( dgs );
3215 gtk_widget_destroy ( dialog );
3217 // When called from the viewport - work the corresponding layerspanel:
3219 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3222 // Apply settings to transfer to the GPS device
3229 vik_layers_panel_get_viewport (vlp),
3238 * Acquire into this TRW Layer from any GPS Babel supported file
3240 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3242 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3243 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3244 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3245 VikViewport *vvp = vik_window_viewport(vw);
3247 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3250 static void trw_layer_new_wp ( gpointer lav[2] )
3252 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3253 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3254 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3255 instead return true if you want to update. */
3256 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 ) {
3257 trw_layer_calculate_bounds_waypoints ( vtl );
3258 vik_layers_panel_emit_update ( vlp );
3262 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3264 vtl->current_track = vik_track_new();
3265 vtl->current_track->visible = TRUE;
3266 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3267 // Create track with the preferred colour from the layer properties
3268 vtl->current_track->color = vtl->track_color;
3270 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3271 vtl->current_track->has_color = TRUE;
3272 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3275 static void trw_layer_new_track ( gpointer lav[2] )
3277 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3279 if ( ! vtl->current_track ) {
3280 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3281 new_track_create_common ( vtl, name );
3284 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3288 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3290 vtl->current_track = vik_track_new();
3291 vtl->current_track->visible = TRUE;
3292 vtl->current_track->is_route = TRUE;
3293 // By default make all routes red
3294 vtl->current_track->has_color = TRUE;
3295 gdk_color_parse ( "red", &vtl->current_track->color );
3296 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3299 static void trw_layer_new_route ( gpointer lav[2] )
3301 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3303 if ( ! vtl->current_track ) {
3304 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3305 new_route_create_common ( vtl, name );
3307 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3311 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3313 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3314 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3316 if ( g_hash_table_size (vtl->routes) > 0 ) {
3317 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3318 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3319 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3320 vik_layers_panel_emit_update ( vlp );
3325 static void trw_layer_finish_track ( gpointer lav[2] )
3327 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3328 vtl->current_track = NULL;
3329 vik_layer_emit_update ( VIK_LAYER(vtl) );
3332 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3334 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3335 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3337 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3338 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3339 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3340 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3341 vik_layers_panel_emit_update ( vlp );
3345 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3347 /* NB do not care if wp is visible or not */
3348 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3351 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3353 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3354 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3356 /* Only 1 waypoint - jump straight to it */
3357 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3358 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3359 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3361 /* If at least 2 waypoints - find center and then zoom to fit */
3362 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3364 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3365 maxmin[0].lat = vtl->waypoints_bbox.north;
3366 maxmin[1].lat = vtl->waypoints_bbox.south;
3367 maxmin[0].lon = vtl->waypoints_bbox.east;
3368 maxmin[1].lon = vtl->waypoints_bbox.west;
3369 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3372 vik_layers_panel_emit_update ( vlp );
3375 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3377 static gpointer pass_along[2];
3379 GtkWidget *export_submenu;
3380 pass_along[0] = vtl;
3381 pass_along[1] = vlp;
3383 item = gtk_menu_item_new();
3384 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3385 gtk_widget_show ( item );
3387 if ( vtl->current_track ) {
3388 if ( vtl->current_track->is_route )
3389 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3391 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3392 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3393 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3394 gtk_widget_show ( item );
3397 item = gtk_menu_item_new ();
3398 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3399 gtk_widget_show ( item );
3402 /* Now with icons */
3403 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3404 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3406 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3407 gtk_widget_show ( item );
3409 GtkWidget *view_submenu = gtk_menu_new();
3410 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3411 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3412 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3413 gtk_widget_show ( item );
3414 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3416 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3417 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3418 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3419 gtk_widget_show ( item );
3421 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3423 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3424 gtk_widget_show ( item );
3426 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3428 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3429 gtk_widget_show ( item );
3431 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3432 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3434 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3435 gtk_widget_show ( item );
3437 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3438 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3439 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3440 gtk_widget_show ( item );
3442 export_submenu = gtk_menu_new ();
3443 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3444 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3445 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3446 gtk_widget_show ( item );
3447 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3449 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3450 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3451 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3452 gtk_widget_show ( item );
3454 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3456 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3457 gtk_widget_show ( item );
3459 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3460 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3461 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3462 gtk_widget_show ( item );
3464 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3466 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3467 gtk_widget_show ( item );
3469 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3470 item = gtk_menu_item_new_with_mnemonic ( external1 );
3471 g_free ( external1 );
3472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3473 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3474 gtk_widget_show ( item );
3476 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3477 item = gtk_menu_item_new_with_mnemonic ( external2 );
3478 g_free ( external2 );
3479 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3480 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3481 gtk_widget_show ( item );
3483 GtkWidget *new_submenu = gtk_menu_new();
3484 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3485 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3486 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3487 gtk_widget_show(item);
3488 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3490 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3491 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3493 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3494 gtk_widget_show ( item );
3496 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3499 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3500 gtk_widget_show ( item );
3501 // Make it available only when a new track *not* already in progress
3502 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3504 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3507 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3508 gtk_widget_show ( item );
3509 // Make it available only when a new track *not* already in progress
3510 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3512 #ifdef VIK_CONFIG_GEOTAG
3513 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3515 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3516 gtk_widget_show ( item );
3519 GtkWidget *acquire_submenu = gtk_menu_new ();
3520 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3521 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3522 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3523 gtk_widget_show ( item );
3524 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3526 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3527 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3528 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3529 gtk_widget_show ( item );
3531 /* FIXME: only add menu when at least a routing engine has support for Directions */
3532 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3533 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3534 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3535 gtk_widget_show ( item );
3537 #ifdef VIK_CONFIG_OPENSTREETMAP
3538 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3540 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3541 gtk_widget_show ( item );
3543 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3544 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3545 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3546 gtk_widget_show ( item );
3549 #ifdef VIK_CONFIG_GEONAMES
3550 GtkWidget *wikipedia_submenu = gtk_menu_new();
3551 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3552 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3553 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3554 gtk_widget_show(item);
3555 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3557 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3558 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3560 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3561 gtk_widget_show ( item );
3563 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3566 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3567 gtk_widget_show ( item );
3570 #ifdef VIK_CONFIG_GEOCACHES
3571 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3572 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3573 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3574 gtk_widget_show ( item );
3577 #ifdef VIK_CONFIG_GEOTAG
3578 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3580 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3581 gtk_widget_show ( item );
3584 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3585 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3586 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3587 gtk_widget_show ( item );
3589 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3591 GtkWidget *upload_submenu = gtk_menu_new ();
3592 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3593 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3594 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3595 gtk_widget_show ( item );
3596 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3598 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3599 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3600 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3601 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3602 gtk_widget_show ( item );
3604 #ifdef VIK_CONFIG_OPENSTREETMAP
3605 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3606 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3607 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3608 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3609 gtk_widget_show ( item );
3612 GtkWidget *delete_submenu = gtk_menu_new ();
3613 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3614 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3615 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3616 gtk_widget_show ( item );
3617 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3619 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3620 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3621 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3622 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3623 gtk_widget_show ( item );
3625 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3626 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3627 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3628 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3629 gtk_widget_show ( item );
3631 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3632 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3633 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3634 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3635 gtk_widget_show ( item );
3637 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3638 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3639 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3640 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3641 gtk_widget_show ( item );
3643 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3644 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3645 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3646 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3647 gtk_widget_show ( item );
3649 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3650 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3651 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3652 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3653 gtk_widget_show ( item );
3655 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3656 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3658 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3659 gtk_widget_show ( item );
3662 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3663 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3665 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3666 gtk_widget_show ( item );
3670 // Fake Waypoint UUIDs vi simple increasing integer
3671 static guint wp_uuid = 0;
3673 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3677 vik_waypoint_set_name (wp, name);
3679 if ( VIK_LAYER(vtl)->realized )
3681 // Do we need to create the sublayer:
3682 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3683 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3686 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3688 // Visibility column always needed for waypoints
3689 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 );
3691 // Actual setting of visibility dependent on the waypoint
3692 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3694 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3696 // Sort now as post_read is not called on a realized waypoint
3697 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
3700 highest_wp_number_add_wp(vtl, name);
3701 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3705 // Fake Track UUIDs vi simple increasing integer
3706 static guint tr_uuid = 0;
3708 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3712 vik_track_set_name (t, name);
3714 if ( VIK_LAYER(vtl)->realized )
3716 // Do we need to create the sublayer:
3717 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3718 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3721 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3722 // Visibility column always needed for tracks
3723 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 );
3725 // Actual setting of visibility dependent on the track
3726 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3728 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3730 // Sort now as post_read is not called on a realized track
3731 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
3734 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3736 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3739 // Fake Route UUIDs vi simple increasing integer
3740 static guint rt_uuid = 0;
3742 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3746 vik_track_set_name (t, name);
3748 if ( VIK_LAYER(vtl)->realized )
3750 // Do we need to create the sublayer:
3751 if ( g_hash_table_size (vtl->routes) == 0 ) {
3752 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3755 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3756 // Visibility column always needed for routes
3757 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 );
3758 // Actual setting of visibility dependent on the route
3759 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3761 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3763 // Sort now as post_read is not called on a realized route
3764 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
3767 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3769 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3772 /* to be called whenever a track has been deleted or may have been changed. */
3773 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3775 if (vtl->current_tp_track == trk )
3776 trw_layer_cancel_current_tp ( vtl, FALSE );
3780 * Normally this is done to due the waypoint size preference having changed
3782 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3784 GHashTableIter iter;
3785 gpointer key, value;
3788 g_hash_table_iter_init ( &iter, vtl->waypoints );
3789 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3790 VikWaypoint *wp = VIK_WAYPOINT(value);
3792 // Reapply symbol setting to update the pixbuf
3793 gchar *tmp_symbol = g_strdup ( wp->symbol );
3794 vik_waypoint_set_symbol ( wp, tmp_symbol );
3795 g_free ( tmp_symbol );
3801 * trw_layer_new_unique_sublayer_name:
3803 * Allocates a unique new name
3805 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3808 gchar *newname = g_strdup(name);
3813 switch ( sublayer_type ) {
3814 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3815 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3817 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3818 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3821 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3824 // If found a name already in use try adding 1 to it and we try again
3826 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3828 newname = new_newname;
3831 } while ( id != NULL);
3836 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3838 // No more uniqueness of name forced when loading from a file
3839 // This now makes this function a little redunant as we just flow the parameters through
3840 vik_trw_layer_add_waypoint ( vtl, name, wp );
3843 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3845 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3846 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3847 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3848 vik_track_free ( tr );
3849 vtl->route_finder_append = FALSE; /* this means we have added it */
3852 // No more uniqueness of name forced when loading from a file
3854 vik_trw_layer_add_route ( vtl, name, tr );
3856 vik_trw_layer_add_track ( vtl, name, tr );
3858 if ( vtl->route_finder_check_added_track ) {
3859 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3860 vtl->route_finder_added_track = tr;
3865 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3867 *l = g_list_append(*l, id);
3871 * Move an item from one TRW layer to another TRW layer
3873 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3875 // TODO reconsider strategy when moving within layer (if anything...)
3876 gboolean rename = ( vtl_src != vtl_dest );
3880 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3881 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3885 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3887 newname = g_strdup ( trk->name );
3889 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3890 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3892 vik_trw_layer_delete_track ( vtl_src, trk );
3895 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3896 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3900 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3902 newname = g_strdup ( trk->name );
3904 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3905 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3907 vik_trw_layer_delete_route ( vtl_src, trk );
3910 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3911 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3915 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
3917 newname = g_strdup ( wp->name );
3919 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3920 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3922 trw_layer_delete_waypoint ( vtl_src, wp );
3924 // Recalculate bounds even if not renamed as maybe dragged between layers
3925 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3926 trw_layer_calculate_bounds_waypoints ( vtl_src );
3930 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3932 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3933 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3935 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3936 GList *items = NULL;
3939 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3940 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3942 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3943 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3945 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3946 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3951 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3952 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3953 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3954 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3956 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3963 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3964 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3969 VikTrack *trk; // input
3970 gpointer uuid; // output
3973 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3975 trku_udata *user_data = udata;
3976 if ( trk == user_data->trk ) {
3977 user_data->uuid = id;
3983 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3985 gboolean was_visible = FALSE;
3987 if ( trk && trk->name ) {
3989 if ( trk == vtl->current_track ) {
3990 vtl->current_track = NULL;
3991 vtl->current_tp_track = NULL;
3992 vtl->current_tp_id = NULL;
3993 vtl->moving_tp = FALSE;
3996 was_visible = trk->visible;
3998 if ( trk == vtl->route_finder_current_track )
3999 vtl->route_finder_current_track = NULL;
4001 if ( trk == vtl->route_finder_added_track )
4002 vtl->route_finder_added_track = NULL;
4008 // Hmmm, want key of it
4009 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4011 if ( trkf && udata.uuid ) {
4012 /* could be current_tp, so we have to check */
4013 trw_layer_cancel_tps_of_track ( vtl, trk );
4015 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4018 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4019 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4020 g_hash_table_remove ( vtl->tracks, udata.uuid );
4022 // If last sublayer, then remove sublayer container
4023 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4024 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4032 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4034 gboolean was_visible = FALSE;
4036 if ( trk && trk->name ) {
4038 if ( trk == vtl->current_track ) {
4039 vtl->current_track = NULL;
4040 vtl->current_tp_track = NULL;
4041 vtl->current_tp_id = NULL;
4042 vtl->moving_tp = FALSE;
4045 was_visible = trk->visible;
4047 if ( trk == vtl->route_finder_current_track )
4048 vtl->route_finder_current_track = NULL;
4050 if ( trk == vtl->route_finder_added_track )
4051 vtl->route_finder_added_track = NULL;
4057 // Hmmm, want key of it
4058 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4060 if ( trkf && udata.uuid ) {
4061 /* could be current_tp, so we have to check */
4062 trw_layer_cancel_tps_of_track ( vtl, trk );
4064 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4067 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4068 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4069 g_hash_table_remove ( vtl->routes, udata.uuid );
4071 // If last sublayer, then remove sublayer container
4072 if ( g_hash_table_size (vtl->routes) == 0 ) {
4073 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4081 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4083 gboolean was_visible = FALSE;
4085 if ( wp && wp->name ) {
4087 if ( wp == vtl->current_wp ) {
4088 vtl->current_wp = NULL;
4089 vtl->current_wp_id = NULL;
4090 vtl->moving_wp = FALSE;
4093 was_visible = wp->visible;
4099 // Hmmm, want key of it
4100 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4102 if ( wpf && udata.uuid ) {
4103 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4106 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4107 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4109 highest_wp_number_remove_wp(vtl, wp->name);
4110 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4112 // If last sublayer, then remove sublayer container
4113 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4114 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4124 // Only for temporary use by trw_layer_delete_waypoint_by_name
4125 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4127 wpu_udata *user_data = udata;
4128 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4129 user_data->uuid = id;
4136 * Delete a waypoint by the given name
4137 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4138 * as there be multiple waypoints with the same name
4140 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4143 // Fake a waypoint with the given name
4144 udata.wp = vik_waypoint_new ();
4145 vik_waypoint_set_name (udata.wp, name);
4146 // Currently only the name is used in this waypoint find function
4149 // Hmmm, want key of it
4150 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4152 vik_waypoint_free (udata.wp);
4154 if ( wpf && udata.uuid )
4155 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4161 VikTrack *trk; // input
4162 gpointer uuid; // output
4165 // Only for temporary use by trw_layer_delete_track_by_name
4166 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4168 tpu_udata *user_data = udata;
4169 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4170 user_data->uuid = id;
4177 * Delete a track by the given name
4178 * NOTE: ATM this will delete the first encountered Track with the specified name
4179 * as there may be multiple tracks with the same name within the specified hash table
4181 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4184 // Fake a track with the given name
4185 udata.trk = vik_track_new ();
4186 vik_track_set_name (udata.trk, name);
4187 // Currently only the name is used in this waypoint find function
4190 // Hmmm, want key of it
4191 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4193 vik_track_free (udata.trk);
4195 if ( trkf && udata.uuid ) {
4196 // This could be a little better written...
4197 if ( vtl->tracks == ht_tracks )
4198 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4199 if ( vtl->routes == ht_tracks )
4200 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4207 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4209 vik_treeview_item_delete (vt, it );
4212 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4215 vtl->current_track = NULL;
4216 vtl->route_finder_current_track = NULL;
4217 vtl->route_finder_added_track = NULL;
4218 if (vtl->current_tp_track)
4219 trw_layer_cancel_current_tp(vtl, FALSE);
4221 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4222 g_hash_table_remove_all(vtl->routes_iters);
4223 g_hash_table_remove_all(vtl->routes);
4225 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4227 vik_layer_emit_update ( VIK_LAYER(vtl) );
4230 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4233 vtl->current_track = NULL;
4234 vtl->route_finder_current_track = NULL;
4235 vtl->route_finder_added_track = NULL;
4236 if (vtl->current_tp_track)
4237 trw_layer_cancel_current_tp(vtl, FALSE);
4239 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4240 g_hash_table_remove_all(vtl->tracks_iters);
4241 g_hash_table_remove_all(vtl->tracks);
4243 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4245 vik_layer_emit_update ( VIK_LAYER(vtl) );
4248 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4250 vtl->current_wp = NULL;
4251 vtl->current_wp_id = NULL;
4252 vtl->moving_wp = FALSE;
4254 highest_wp_number_reset(vtl);
4256 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4257 g_hash_table_remove_all(vtl->waypoints_iters);
4258 g_hash_table_remove_all(vtl->waypoints);
4260 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4262 vik_layer_emit_update ( VIK_LAYER(vtl) );
4265 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4267 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4268 // Get confirmation from the user
4269 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4270 _("Are you sure you want to delete all tracks in %s?"),
4271 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4272 vik_trw_layer_delete_all_tracks (vtl);
4275 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4277 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4278 // Get confirmation from the user
4279 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4280 _("Are you sure you want to delete all routes in %s?"),
4281 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4282 vik_trw_layer_delete_all_routes (vtl);
4285 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4287 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4288 // Get confirmation from the user
4289 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4290 _("Are you sure you want to delete all waypoints in %s?"),
4291 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4292 vik_trw_layer_delete_all_waypoints (vtl);
4295 static void trw_layer_delete_item ( gpointer pass_along[6] )
4297 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4298 gboolean was_visible = FALSE;
4299 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4301 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4302 if ( wp && wp->name ) {
4303 if ( GPOINTER_TO_INT ( pass_along[4]) )
4304 // Get confirmation from the user
4305 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4306 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4307 _("Are you sure you want to delete the waypoint \"%s\""),
4310 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4313 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4315 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4316 if ( trk && trk->name ) {
4317 if ( GPOINTER_TO_INT ( pass_along[4]) )
4318 // Get confirmation from the user
4319 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4320 _("Are you sure you want to delete the track \"%s\""),
4323 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4328 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4329 if ( trk && trk->name ) {
4330 if ( GPOINTER_TO_INT ( pass_along[4]) )
4331 // Get confirmation from the user
4332 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4333 _("Are you sure you want to delete the route \"%s\""),
4336 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4340 vik_layer_emit_update ( VIK_LAYER(vtl) );
4344 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4346 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4348 vik_waypoint_set_name ( wp, new_name );
4350 // Now update the treeview as well
4355 // Need key of it for treeview update
4356 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4358 if ( wpf && udataU.uuid ) {
4359 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4362 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4363 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4368 static void trw_layer_properties_item ( gpointer pass_along[7] )
4370 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4371 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4373 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4375 if ( wp && wp->name )
4377 gboolean updated = FALSE;
4378 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4380 trw_layer_waypoint_rename ( vtl, wp, new_name );
4382 if ( updated && pass_along[6] )
4383 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4385 if ( updated && VIK_LAYER(vtl)->visible )
4386 vik_layer_emit_update ( VIK_LAYER(vtl) );
4392 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4393 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4395 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4397 if ( tr && tr->name )
4399 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4402 pass_along[1], /* vlp */
4403 pass_along[5], /* vvp */
4404 pass_along[6]); /* iter */
4410 * Update the treeview of the track id - primarily to update the icon
4412 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4418 gpointer *trkf = NULL;
4419 if ( trk->is_route )
4420 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4422 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4424 if ( trkf && udata.uuid ) {
4426 GtkTreeIter *iter = NULL;
4427 if ( trk->is_route )
4428 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4430 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4433 // TODO: Make this a function
4434 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4435 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4436 ((trk->color.green & 0xff00) << 8) |
4437 (trk->color.blue & 0xff00);
4438 gdk_pixbuf_fill ( pixbuf, pixel );
4439 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4440 g_object_unref (pixbuf);
4447 Parameter 1 -> VikLayersPanel
4448 Parameter 2 -> VikLayer
4449 Parameter 3 -> VikViewport
4451 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4454 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4455 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4458 /* since vlp not set, vl & vvp should be valid instead! */
4460 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4461 vik_layer_emit_update ( VIK_LAYER(vl) );
4466 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4468 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4470 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4471 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4473 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4475 if ( track && track->trackpoints )
4476 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4479 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4481 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4483 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4484 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4486 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4488 if ( track && track->trackpoints )
4490 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4492 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4493 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4494 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4495 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4496 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4500 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4502 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4504 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4505 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4507 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4512 // Converting a track to a route can be a bit more complicated,
4513 // so give a chance to change our minds:
4514 if ( !trk->is_route &&
4515 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4516 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4518 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4519 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4524 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4527 trk_copy->is_route = !trk_copy->is_route;
4529 // ATM can't set name to self - so must create temporary copy
4530 gchar *name = g_strdup ( trk_copy->name );
4532 // Delete old one and then add new one
4533 if ( trk->is_route ) {
4534 vik_trw_layer_delete_route ( vtl, trk );
4535 vik_trw_layer_add_track ( vtl, name, trk_copy );
4538 // Extra route conversion bits...
4539 vik_track_merge_segments ( trk_copy );
4540 vik_track_to_routepoints ( trk_copy );
4542 vik_trw_layer_delete_track ( vtl, trk );
4543 vik_trw_layer_add_route ( vtl, name, trk_copy );
4547 // Update in case color of track / route changes when moving between sublayers
4548 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4551 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4553 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4555 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4556 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4558 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4561 vik_track_anonymize_times ( track );
4564 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4566 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4568 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4569 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4571 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4576 vtl->current_track = track;
4577 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);
4579 if ( track->trackpoints )
4580 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4584 * extend a track using route finder
4586 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4588 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4589 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4592 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4594 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4595 vtl->route_finder_coord = last_coord;
4596 vtl->route_finder_current_track = track;
4597 vtl->route_finder_started = TRUE;
4599 if ( track->trackpoints )
4600 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4605 * apply_dem_data_common:
4607 * A common function for applying the DEM values and reporting the results.
4609 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
4611 // If have a vlp then perform a basic test to see if any DEM info available...
4613 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4615 if ( !g_list_length(dems) ) {
4616 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
4621 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
4622 // Inform user how much was changed
4624 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4625 g_snprintf(str, 64, tmp_str, changed);
4626 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4629 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
4631 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4633 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4634 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4636 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4639 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
4642 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
4644 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4646 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4647 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4649 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4652 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
4658 * A common function for applying the elevation smoothing and reporting the results.
4660 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
4662 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
4663 // Inform user how much was changed
4665 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4666 g_snprintf(str, 64, tmp_str, changed);
4667 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4673 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
4675 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4677 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4678 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4680 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4685 smooth_it ( vtl, track, FALSE );
4688 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
4690 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4692 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4693 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4695 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4700 smooth_it ( vtl, track, TRUE );
4703 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4705 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4707 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4708 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4710 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4715 GList *trps = track->trackpoints;
4718 trps = g_list_last(trps);
4719 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4722 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4724 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4726 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4727 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4729 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4734 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4737 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4740 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4742 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4744 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4745 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4747 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4752 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4755 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4758 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4760 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4762 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4763 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4765 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4770 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4773 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4777 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4779 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4781 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4783 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4784 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4786 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4788 if ( trk && trk->trackpoints )
4790 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4791 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4792 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
4793 if ( pass_along[1] )
4794 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4796 vik_layer_emit_update ( VIK_LAYER(vtl) );
4801 * Refine the selected track/route with a routing engine.
4802 * The routing engine is selected by the user, when requestiong the job.
4804 static void trw_layer_route_refine ( gpointer pass_along[6] )
4806 static gint last_engine = 0;
4807 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4810 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4811 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4813 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4815 if ( trk && trk->trackpoints )
4817 /* Check size of the route */
4818 int nb = vik_track_get_tp_count(trk);
4820 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
4821 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4822 GTK_MESSAGE_WARNING,
4823 GTK_BUTTONS_OK_CANCEL,
4824 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
4826 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
4827 gtk_widget_destroy ( dialog );
4828 if (response != GTK_RESPONSE_OK )
4831 /* Select engine from dialog */
4832 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
4833 VIK_GTK_WINDOW_FROM_LAYER (vtl),
4834 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4836 GTK_RESPONSE_REJECT,
4838 GTK_RESPONSE_ACCEPT,
4840 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
4841 gtk_widget_show_all(label);
4843 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
4845 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
4846 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
4847 gtk_widget_show_all(combo);
4849 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
4851 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4853 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4855 /* Dialog validated: retrieve selected engine and do the job */
4856 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
4857 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
4860 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
4862 /* Force saving track */
4863 /* FIXME: remove or rename this hack */
4864 vtl->route_finder_check_added_track = TRUE;
4867 vik_routing_engine_refine (routing, vtl, trk);
4869 /* FIXME: remove or rename this hack */
4870 if ( vtl->route_finder_added_track )
4871 vik_track_calculate_bounds ( vtl->route_finder_added_track );
4873 vtl->route_finder_added_track = NULL;
4874 vtl->route_finder_check_added_track = FALSE;
4876 vik_layer_emit_update ( VIK_LAYER(vtl) );
4878 /* Restore cursor */
4879 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
4881 gtk_widget_destroy ( dialog );
4885 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4887 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4888 trw_layer_tpwin_init ( vtl );
4891 /*************************************
4892 * merge/split by time routines
4893 *************************************/
4895 /* called for each key in track hash table.
4896 * If the current track has the same time stamp type, add it to the result,
4897 * except the one pointed by "exclude".
4898 * set exclude to NULL if there is no exclude to check.
4899 * Note that the result is in reverse (for performance reasons).
4904 gboolean with_timestamps;
4906 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4908 twt_udata *user_data = udata;
4909 VikTrackpoint *p1, *p2;
4911 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4915 if (VIK_TRACK(value)->trackpoints) {
4916 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4917 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4919 if ( user_data->with_timestamps ) {
4920 if (!p1->has_timestamp || !p2->has_timestamp) {
4925 // Don't add tracks with timestamps when getting non timestamp tracks
4926 if (p1->has_timestamp || p2->has_timestamp) {
4932 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4935 /* called for each key in track hash table. if original track user_data[1] is close enough
4936 * to the passed one, add it to list in user_data[0]
4938 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4941 VikTrackpoint *p1, *p2;
4942 VikTrack *trk = VIK_TRACK(value);
4944 GList **nearby_tracks = ((gpointer *)user_data)[0];
4945 GList *tpoints = ((gpointer *)user_data)[1];
4948 * detect reasons for not merging, and return
4949 * if no reason is found not to merge, then do it.
4952 // Exclude the original track from the compiled list
4953 if (trk->trackpoints == tpoints) {
4957 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4958 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4960 if (trk->trackpoints) {
4961 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4962 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4964 if (!p1->has_timestamp || !p2->has_timestamp) {
4965 //g_print("no timestamp\n");
4969 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4970 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4971 if (! (abs(t1 - p2->timestamp) < threshold ||
4973 abs(p1->timestamp - t2) < threshold)
4980 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4983 /* comparison function used to sort tracks; a and b are hash table keys */
4984 /* Not actively used - can be restored if needed
4985 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4987 GHashTable *tracks = user_data;
4990 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4991 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4993 if (t1 < t2) return -1;
4994 if (t1 > t2) return 1;
4999 /* comparison function used to sort trackpoints */
5000 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5002 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5004 if (t1 < t2) return -1;
5005 if (t1 > t2) return 1;
5010 * comparison function which can be used to sort tracks or waypoints by name
5012 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5014 const gchar* namea = (const gchar*) a;
5015 const gchar* nameb = (const gchar*) b;
5016 if ( namea == NULL || nameb == NULL)
5019 // Same sort method as used in the vik_treeview_*_alphabetize functions
5020 return strcmp ( namea, nameb );
5024 * Attempt to merge selected track with other tracks specified by the user
5025 * Tracks to merge with must be of the same 'type' as the selected track -
5026 * either all with timestamps, or all without timestamps
5028 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5030 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5031 GList *other_tracks = NULL;
5032 GHashTable *ght_tracks;
5033 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5034 ght_tracks = vtl->routes;
5036 ght_tracks = vtl->tracks;
5038 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5043 if ( !track->trackpoints )
5047 udata.result = &other_tracks;
5048 udata.exclude = track->trackpoints;
5049 // Allow merging with 'similar' time type time tracks
5050 // i.e. either those times, or those without
5051 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5053 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5054 other_tracks = g_list_reverse(other_tracks);
5056 if ( !other_tracks ) {
5057 if ( udata.with_timestamps )
5058 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5060 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5064 // Sort alphabetically for user presentation
5065 // Convert into list of names for usage with dialog function
5066 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5067 GList *other_tracks_names = NULL;
5068 GList *iter = g_list_first ( other_tracks );
5070 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5071 iter = g_list_next ( iter );
5074 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5076 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5080 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5081 g_list_free(other_tracks);
5082 g_list_free(other_tracks_names);
5087 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5088 VikTrack *merge_track;
5089 if ( track->is_route )
5090 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5092 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5095 vik_track_steal_and_append_trackpoints ( track, merge_track );
5096 if ( track->is_route )
5097 vik_trw_layer_delete_route (vtl, merge_track);
5099 vik_trw_layer_delete_track (vtl, merge_track);
5100 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5103 for (l = merge_list; l != NULL; l = g_list_next(l))
5105 g_list_free(merge_list);
5107 vik_layer_emit_update( VIK_LAYER(vtl) );
5111 // c.f. trw_layer_sorted_track_id_by_name_list
5112 // but don't add the specified track to the list (normally current track)
5113 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5115 twt_udata *user_data = udata;
5118 if (trk->trackpoints == user_data->exclude) {
5122 // Sort named list alphabetically
5123 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5127 * Join - this allows combining 'tracks' and 'track routes'
5128 * i.e. doesn't care about whether tracks have consistent timestamps
5129 * ATM can only append one track at a time to the currently selected track
5131 static void trw_layer_append_track ( gpointer pass_along[6] )
5134 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5136 GHashTable *ght_tracks;
5137 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5138 ght_tracks = vtl->routes;
5140 ght_tracks = vtl->tracks;
5142 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5147 GList *other_tracks_names = NULL;
5149 // Sort alphabetically for user presentation
5150 // Convert into list of names for usage with dialog function
5151 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5153 udata.result = &other_tracks_names;
5154 udata.exclude = trk->trackpoints;
5156 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5158 // Note the limit to selecting one track only
5159 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5160 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5161 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5164 trk->is_route ? _("Append Route"): _("Append Track"),
5165 trk->is_route ? _("Select the route to append after the current route") :
5166 _("Select the track to append after the current track") );
5168 g_list_free(other_tracks_names);
5170 // It's a list, but shouldn't contain more than one other track!
5171 if ( append_list ) {
5173 for (l = append_list; l != NULL; l = g_list_next(l)) {
5174 // TODO: at present this uses the first track found by name,
5175 // which with potential multiple same named tracks may not be the one selected...
5176 VikTrack *append_track;
5177 if ( trk->is_route )
5178 append_track = vik_trw_layer_get_route ( vtl, l->data );
5180 append_track = vik_trw_layer_get_track ( vtl, l->data );
5182 if ( append_track ) {
5183 vik_track_steal_and_append_trackpoints ( trk, append_track );
5184 if ( trk->is_route )
5185 vik_trw_layer_delete_route (vtl, append_track);
5187 vik_trw_layer_delete_track (vtl, append_track);
5190 for (l = append_list; l != NULL; l = g_list_next(l))
5192 g_list_free(append_list);
5194 vik_layer_emit_update( VIK_LAYER(vtl) );
5199 * Very similar to trw_layer_append_track for joining
5200 * but this allows selection from the 'other' list
5201 * If a track is selected, then is shows routes and joins the selected one
5202 * If a route is selected, then is shows tracks and joins the selected one
5204 static void trw_layer_append_other ( gpointer pass_along[6] )
5207 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5209 GHashTable *ght_mykind, *ght_others;
5210 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5211 ght_mykind = vtl->routes;
5212 ght_others = vtl->tracks;
5215 ght_mykind = vtl->tracks;
5216 ght_others = vtl->routes;
5219 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5224 GList *other_tracks_names = NULL;
5226 // Sort alphabetically for user presentation
5227 // Convert into list of names for usage with dialog function
5228 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5230 udata.result = &other_tracks_names;
5231 udata.exclude = trk->trackpoints;
5233 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5235 // Note the limit to selecting one track only
5236 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5237 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5238 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5241 trk->is_route ? _("Append Track"): _("Append Route"),
5242 trk->is_route ? _("Select the track to append after the current route") :
5243 _("Select the route to append after the current track") );
5245 g_list_free(other_tracks_names);
5247 // It's a list, but shouldn't contain more than one other track!
5248 if ( append_list ) {
5250 for (l = append_list; l != NULL; l = g_list_next(l)) {
5251 // TODO: at present this uses the first track found by name,
5252 // which with potential multiple same named tracks may not be the one selected...
5254 // Get FROM THE OTHER TYPE list
5255 VikTrack *append_track;
5256 if ( trk->is_route )
5257 append_track = vik_trw_layer_get_track ( vtl, l->data );
5259 append_track = vik_trw_layer_get_route ( vtl, l->data );
5261 if ( append_track ) {
5263 if ( !append_track->is_route &&
5264 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5265 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5267 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5268 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5269 vik_track_merge_segments ( append_track );
5270 vik_track_to_routepoints ( append_track );
5277 vik_track_steal_and_append_trackpoints ( trk, append_track );
5279 // Delete copied which is FROM THE OTHER TYPE list
5280 if ( trk->is_route )
5281 vik_trw_layer_delete_track (vtl, append_track);
5283 vik_trw_layer_delete_route (vtl, append_track);
5286 for (l = append_list; l != NULL; l = g_list_next(l))
5288 g_list_free(append_list);
5289 vik_layer_emit_update( VIK_LAYER(vtl) );
5293 /* merge by segments */
5294 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5296 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5297 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5298 guint segments = vik_track_merge_segments ( trk );
5299 // NB currently no need to redraw as segments not actually shown on the display
5300 // However inform the user of what happened:
5302 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5303 g_snprintf(str, 64, tmp_str, segments);
5304 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5307 /* merge by time routine */
5308 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5310 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5314 GList *tracks_with_timestamp = NULL;
5315 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5316 if (orig_trk->trackpoints &&
5317 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5318 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5323 udata.result = &tracks_with_timestamp;
5324 udata.exclude = orig_trk->trackpoints;
5325 udata.with_timestamps = TRUE;
5326 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5327 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5329 if (!tracks_with_timestamp) {
5330 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5333 g_list_free(tracks_with_timestamp);
5335 static guint threshold_in_minutes = 1;
5336 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5337 _("Merge Threshold..."),
5338 _("Merge when time between tracks less than:"),
5339 &threshold_in_minutes)) {
5343 // keep attempting to merge all tracks until no merges within the time specified is possible
5344 gboolean attempt_merge = TRUE;
5345 GList *nearby_tracks = NULL;
5347 static gpointer params[3];
5349 while ( attempt_merge ) {
5351 // Don't try again unless tracks have changed
5352 attempt_merge = FALSE;
5354 trps = orig_trk->trackpoints;
5358 if (nearby_tracks) {
5359 g_list_free(nearby_tracks);
5360 nearby_tracks = NULL;
5363 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5364 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5366 /* g_print("Original track times: %d and %d\n", t1, t2); */
5367 params[0] = &nearby_tracks;
5368 params[1] = (gpointer)trps;
5369 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5371 /* get a list of adjacent-in-time tracks */
5372 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5375 GList *l = nearby_tracks;
5378 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5379 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5381 t1 = get_first_trackpoint(l)->timestamp;
5382 t2 = get_last_trackpoint(l)->timestamp;
5383 #undef get_first_trackpoint
5384 #undef get_last_trackpoint
5385 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5388 /* remove trackpoints from merged track, delete track */
5389 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5390 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5392 // Tracks have changed, therefore retry again against all the remaining tracks
5393 attempt_merge = TRUE;
5398 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5401 g_list_free(nearby_tracks);
5403 vik_layer_emit_update( VIK_LAYER(vtl) );
5407 * Split a track at the currently selected trackpoint
5409 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5411 if ( !vtl->current_tpl )
5414 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5415 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5417 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5418 GList *newglist = g_list_alloc ();
5419 newglist->prev = NULL;
5420 newglist->next = vtl->current_tpl->next;
5421 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5422 tr->trackpoints = newglist;
5424 vtl->current_tpl->next->prev = newglist; /* end old track here */
5425 vtl->current_tpl->next = NULL;
5427 // Bounds of the selected track changed due to the split
5428 vik_track_calculate_bounds ( vtl->current_tp_track );
5430 vtl->current_tpl = newglist; /* change tp to first of new track. */
5431 vtl->current_tp_track = tr;
5434 vik_trw_layer_add_route ( vtl, name, tr );
5436 vik_trw_layer_add_track ( vtl, name, tr );
5438 // Bounds of the new track created by the split
5439 vik_track_calculate_bounds ( tr );
5445 // Also need id of newly created track
5448 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5450 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5452 if ( trkf && udata.uuid )
5453 vtl->current_tp_id = udata.uuid;
5455 vtl->current_tp_id = NULL;
5457 vik_layer_emit_update(VIK_LAYER(vtl));
5463 /* split by time routine */
5464 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5466 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5467 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5468 GList *trps = track->trackpoints;
5470 GList *newlists = NULL;
5471 GList *newtps = NULL;
5472 static guint thr = 1;
5479 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5480 _("Split Threshold..."),
5481 _("Split when time between trackpoints exceeds:"),
5486 /* iterate through trackpoints, and copy them into new lists without touching original list */
5487 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5491 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5493 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5496 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5497 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5498 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5500 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5505 if (ts - prev_ts > thr*60) {
5506 /* flush accumulated trackpoints into new list */
5507 newlists = g_list_append(newlists, g_list_reverse(newtps));
5511 /* accumulate trackpoint copies in newtps, in reverse order */
5512 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5514 iter = g_list_next(iter);
5517 newlists = g_list_append(newlists, g_list_reverse(newtps));
5520 /* put lists of trackpoints into tracks */
5522 // Only bother updating if the split results in new tracks
5523 if (g_list_length (newlists) > 1) {
5528 tr = vik_track_copy ( track, FALSE );
5529 tr->trackpoints = (GList *)(iter->data);
5531 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5532 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5533 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5534 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5535 g_free ( new_tr_name );
5536 vik_track_calculate_bounds ( tr );
5537 iter = g_list_next(iter);
5539 // Remove original track and then update the display
5540 vik_trw_layer_delete_track (vtl, track);
5541 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5543 g_list_free(newlists);
5547 * Split a track by the number of points as specified by the user
5549 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5551 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5553 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5554 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5556 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5561 // Check valid track
5562 GList *trps = track->trackpoints;
5566 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5567 _("Split Every Nth Point"),
5568 _("Split on every Nth point:"),
5569 250, // Default value as per typical limited track capacity of various GPS devices
5573 // Was a valid number returned?
5579 GList *newlists = NULL;
5580 GList *newtps = NULL;
5585 /* accumulate trackpoint copies in newtps, in reverse order */
5586 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5588 if (count >= points) {
5589 /* flush accumulated trackpoints into new list */
5590 newlists = g_list_append(newlists, g_list_reverse(newtps));
5594 iter = g_list_next(iter);
5597 // If there is a remaining chunk put that into the new split list
5598 // This may well be the whole track if no split points were encountered
5600 newlists = g_list_append(newlists, g_list_reverse(newtps));
5603 /* put lists of trackpoints into tracks */
5605 // Only bother updating if the split results in new tracks
5606 if (g_list_length (newlists) > 1) {
5611 tr = vik_track_copy ( track, FALSE );
5612 tr->trackpoints = (GList *)(iter->data);
5614 if ( track->is_route ) {
5615 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5616 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5619 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5620 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5622 g_free ( new_tr_name );
5623 vik_track_calculate_bounds ( tr );
5625 iter = g_list_next(iter);
5627 // Remove original track and then update the display
5628 if ( track->is_route )
5629 vik_trw_layer_delete_route (vtl, track);
5631 vik_trw_layer_delete_track (vtl, track);
5632 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5634 g_list_free(newlists);
5638 * Split a track at the currently selected trackpoint
5640 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5642 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5643 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5644 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5648 * Split a track by its segments
5649 * Routes do not have segments so don't call this for routes
5651 static void trw_layer_split_segments ( gpointer pass_along[6] )
5653 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5654 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5661 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5664 for ( i = 0; i < ntracks; i++ ) {
5666 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5667 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5668 g_free ( new_tr_name );
5673 // Remove original track
5674 vik_trw_layer_delete_track ( vtl, trk );
5675 vik_layer_emit_update ( VIK_LAYER(vtl) );
5678 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5681 /* end of split/merge routines */
5684 * Delete adjacent track points at the same position
5685 * AKA Delete Dulplicates on the Properties Window
5687 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5689 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5691 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5692 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5694 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5699 gulong removed = vik_track_remove_dup_points ( trk );
5701 // Track has been updated so update tps:
5702 trw_layer_cancel_tps_of_track ( vtl, trk );
5704 // Inform user how much was deleted as it's not obvious from the normal view
5706 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5707 g_snprintf(str, 64, tmp_str, removed);
5708 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5710 vik_layer_emit_update ( VIK_LAYER(vtl) );
5714 * Delete adjacent track points with the same timestamp
5715 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5717 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5719 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5721 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5722 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5724 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5729 gulong removed = vik_track_remove_same_time_points ( trk );
5731 // Track has been updated so update tps:
5732 trw_layer_cancel_tps_of_track ( vtl, trk );
5734 // Inform user how much was deleted as it's not obvious from the normal view
5736 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5737 g_snprintf(str, 64, tmp_str, removed);
5738 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5740 vik_layer_emit_update ( VIK_LAYER(vtl) );
5746 static void trw_layer_reverse ( gpointer pass_along[6] )
5748 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5750 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5751 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5753 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5758 vik_track_reverse ( track );
5760 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5764 * Similar to trw_layer_enum_item, but this uses a sorted method
5767 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5769 GList **list = (GList**)udata;
5770 // *list = g_list_prepend(*all, key); //unsorted method
5771 // Sort named list alphabetically
5772 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5777 * Now Waypoint specific sort
5779 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5781 GList **list = (GList**)udata;
5782 // Sort named list alphabetically
5783 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5787 * Track specific sort
5789 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5791 GList **list = (GList**)udata;
5792 // Sort named list alphabetically
5793 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5798 gboolean has_same_track_name;
5799 const gchar *same_track_name;
5800 } same_track_name_udata;
5802 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5804 const gchar* namea = (const gchar*) aa;
5805 const gchar* nameb = (const gchar*) bb;
5808 gint result = strcmp ( namea, nameb );
5810 if ( result == 0 ) {
5811 // Found two names the same
5812 same_track_name_udata *user_data = udata;
5813 user_data->has_same_track_name = TRUE;
5814 user_data->same_track_name = namea;
5817 // Leave ordering the same
5822 * Find out if any tracks have the same name in this hash table
5824 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5826 // Sort items by name, then compare if any next to each other are the same
5828 GList *track_names = NULL;
5829 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5832 if ( ! track_names )
5835 same_track_name_udata udata;
5836 udata.has_same_track_name = FALSE;
5838 // Use sort routine to traverse list comparing items
5839 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5840 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5841 // Still no tracks...
5845 return udata.has_same_track_name;
5849 * Force unqiue track names for the track table specified
5850 * Note the panel is a required parameter to enable the update of the names displayed
5851 * Specify if on tracks or else on routes
5853 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5855 // . Search list for an instance of repeated name
5856 // . get track of this name
5857 // . create new name
5858 // . rename track & update equiv. treeview iter
5859 // . repeat until all different
5861 same_track_name_udata udata;
5863 GList *track_names = NULL;
5864 udata.has_same_track_name = FALSE;
5865 udata.same_track_name = NULL;
5867 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5870 if ( ! track_names )
5873 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5875 // Still no tracks...
5876 if ( ! dummy_list1 )
5879 while ( udata.has_same_track_name ) {
5881 // Find a track with the same name
5884 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5886 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5890 g_critical("Houston, we've had a problem.");
5891 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5892 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5897 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5898 vik_track_set_name ( trk, newname );
5904 // Need want key of it for treeview update
5905 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5907 if ( trkf && udataU.uuid ) {
5911 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5913 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5916 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5918 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
5920 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
5924 // Start trying to find same names again...
5926 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5927 udata.has_same_track_name = FALSE;
5928 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5930 // No tracks any more - give up searching
5931 if ( ! dummy_list2 )
5932 udata.has_same_track_name = FALSE;
5936 vik_layers_panel_emit_update ( vlp );
5939 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
5941 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5944 switch (GPOINTER_TO_INT (pass_along[2])) {
5945 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5946 iter = &(vtl->tracks_iter);
5947 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5949 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5950 iter = &(vtl->routes_iter);
5951 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5953 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5954 iter = &(vtl->waypoints_iter);
5955 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5959 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
5962 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
5964 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5967 switch (GPOINTER_TO_INT (pass_along[2])) {
5968 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5969 iter = &(vtl->tracks_iter);
5970 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5972 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5973 iter = &(vtl->routes_iter);
5974 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5976 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5977 iter = &(vtl->waypoints_iter);
5978 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5982 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
5988 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5990 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5993 // Ensure list of track names offered is unique
5994 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5995 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5996 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5997 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6003 // Sort list alphabetically for better presentation
6004 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6007 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6011 // Get list of items to delete from the user
6012 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6015 _("Delete Selection"),
6016 _("Select tracks to delete"));
6019 // Delete requested tracks
6020 // since specificly requested, IMHO no need for extra confirmation
6021 if ( delete_list ) {
6023 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6024 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6025 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6027 g_list_free(delete_list);
6028 vik_layer_emit_update( VIK_LAYER(vtl) );
6035 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6037 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6040 // Ensure list of track names offered is unique
6041 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6042 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6043 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6044 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6050 // Sort list alphabetically for better presentation
6051 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6054 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6058 // Get list of items to delete from the user
6059 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6062 _("Delete Selection"),
6063 _("Select routes to delete") );
6066 // Delete requested routes
6067 // since specificly requested, IMHO no need for extra confirmation
6068 if ( delete_list ) {
6070 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6071 // This deletes first route it finds of that name (but uniqueness is enforced above)
6072 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6074 g_list_free(delete_list);
6075 vik_layer_emit_update( VIK_LAYER(vtl) );
6080 gboolean has_same_waypoint_name;
6081 const gchar *same_waypoint_name;
6082 } same_waypoint_name_udata;
6084 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6086 const gchar* namea = (const gchar*) aa;
6087 const gchar* nameb = (const gchar*) bb;
6090 gint result = strcmp ( namea, nameb );
6092 if ( result == 0 ) {
6093 // Found two names the same
6094 same_waypoint_name_udata *user_data = udata;
6095 user_data->has_same_waypoint_name = TRUE;
6096 user_data->same_waypoint_name = namea;
6099 // Leave ordering the same
6104 * Find out if any waypoints have the same name in this layer
6106 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6108 // Sort items by name, then compare if any next to each other are the same
6110 GList *waypoint_names = NULL;
6111 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6114 if ( ! waypoint_names )
6117 same_waypoint_name_udata udata;
6118 udata.has_same_waypoint_name = FALSE;
6120 // Use sort routine to traverse list comparing items
6121 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6122 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6123 // Still no waypoints...
6127 return udata.has_same_waypoint_name;
6131 * Force unqiue waypoint names for this layer
6132 * Note the panel is a required parameter to enable the update of the names displayed
6134 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6136 // . Search list for an instance of repeated name
6137 // . get waypoint of this name
6138 // . create new name
6139 // . rename waypoint & update equiv. treeview iter
6140 // . repeat until all different
6142 same_waypoint_name_udata udata;
6144 GList *waypoint_names = NULL;
6145 udata.has_same_waypoint_name = FALSE;
6146 udata.same_waypoint_name = NULL;
6148 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6151 if ( ! waypoint_names )
6154 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6156 // Still no waypoints...
6157 if ( ! dummy_list1 )
6160 while ( udata.has_same_waypoint_name ) {
6162 // Find a waypoint with the same name
6163 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6167 g_critical("Houston, we've had a problem.");
6168 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6169 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6174 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6176 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6178 // Start trying to find same names again...
6179 waypoint_names = NULL;
6180 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6181 udata.has_same_waypoint_name = FALSE;
6182 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6184 // No waypoints any more - give up searching
6185 if ( ! dummy_list2 )
6186 udata.has_same_waypoint_name = FALSE;
6190 vik_layers_panel_emit_update ( vlp );
6196 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6198 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6201 // Ensure list of waypoint names offered is unique
6202 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6203 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6204 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6205 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6211 // Sort list alphabetically for better presentation
6212 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6214 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6218 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6220 // Get list of items to delete from the user
6221 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6224 _("Delete Selection"),
6225 _("Select waypoints to delete"));
6228 // Delete requested waypoints
6229 // since specificly requested, IMHO no need for extra confirmation
6230 if ( delete_list ) {
6232 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6233 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6234 trw_layer_delete_waypoint_by_name (vtl, l->data);
6236 g_list_free(delete_list);
6238 trw_layer_calculate_bounds_waypoints ( vtl );
6239 vik_layer_emit_update( VIK_LAYER(vtl) );
6247 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6249 vik_treeview_item_toggle_visible ( vt, it );
6255 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6257 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6263 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6265 wp->visible = GPOINTER_TO_INT (on_off);
6271 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6273 wp->visible = !wp->visible;
6279 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6281 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6282 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6283 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6284 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6286 vik_layer_emit_update ( VIK_LAYER(vtl) );
6292 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6294 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6295 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6296 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6297 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6299 vik_layer_emit_update ( VIK_LAYER(vtl) );
6305 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6307 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6308 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6309 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6311 vik_layer_emit_update ( VIK_LAYER(vtl) );
6317 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6319 trk->visible = GPOINTER_TO_INT (on_off);
6325 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6327 trk->visible = !trk->visible;
6333 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6335 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6336 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6337 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6338 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6340 vik_layer_emit_update ( VIK_LAYER(vtl) );
6346 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6348 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6349 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6350 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6351 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6353 vik_layer_emit_update ( VIK_LAYER(vtl) );
6359 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6361 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6362 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6363 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6365 vik_layer_emit_update ( VIK_LAYER(vtl) );
6371 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6373 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6374 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6375 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6376 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6378 vik_layer_emit_update ( VIK_LAYER(vtl) );
6384 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6386 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6387 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6388 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6389 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6391 vik_layer_emit_update ( VIK_LAYER(vtl) );
6397 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6399 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6400 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6401 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6403 vik_layer_emit_update ( VIK_LAYER(vtl) );
6407 * trw_layer_analyse_close:
6409 * Stuff to do on dialog closure
6411 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6413 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6414 gtk_widget_destroy ( dialog );
6415 vtl->tracks_analysis_dialog = NULL;
6419 * trw_layer_analyse_create_list:
6421 * Create the latest list of tracks with the associated layer(s)
6422 * Although this will always be from a single layer here
6424 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6426 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6427 GList *tracks = NULL;
6428 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6429 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6431 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6433 GList *tracks_and_layers = NULL;
6434 // build tracks_and_layers list
6435 tracks = g_list_first ( tracks );
6437 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6438 vtdl->trk = VIK_TRACK(tracks->data);
6440 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6441 tracks = g_list_next ( tracks );
6444 return tracks_and_layers;
6447 static void trw_layer_tracks_stats ( gpointer lav[2] )
6449 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6450 // There can only be one!
6451 if ( vtl->tracks_analysis_dialog )
6454 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6455 VIK_LAYER(vtl)->name,
6457 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6458 trw_layer_analyse_create_list,
6459 trw_layer_analyse_close );
6465 static void trw_layer_routes_stats ( gpointer lav[2] )
6467 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6468 // There can only be one!
6469 if ( vtl->tracks_analysis_dialog )
6472 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6473 VIK_LAYER(vtl)->name,
6475 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6476 trw_layer_analyse_create_list,
6477 trw_layer_analyse_close );
6480 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6482 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6484 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6487 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6489 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6492 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6493 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6497 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6499 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6502 if ( !strncmp(wp->comment, "http", 4) ) {
6503 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6504 } else if ( !strncmp(wp->description, "http", 4) ) {
6505 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6509 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6511 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6513 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6515 // No actual change to the name supplied
6517 if (strcmp(newname, wp->name) == 0 )
6520 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6523 // An existing waypoint has been found with the requested name
6524 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6525 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6530 // Update WP name and refresh the treeview
6531 vik_waypoint_set_name (wp, newname);
6533 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6534 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6536 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6541 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6543 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6545 // No actual change to the name supplied
6547 if (strcmp(newname, trk->name) == 0)
6550 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6553 // An existing track has been found with the requested name
6554 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6555 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6559 // Update track name and refresh GUI parts
6560 vik_track_set_name (trk, newname);
6562 // Update any subwindows that could be displaying this track which has changed name
6563 // Only one Track Edit Window
6564 if ( l->current_tp_track == trk && l->tpwin ) {
6565 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6567 // Property Dialog of the track
6568 vik_trw_layer_propwin_update ( trk );
6570 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6571 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6573 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6578 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6580 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6582 // No actual change to the name supplied
6584 if (strcmp(newname, trk->name) == 0)
6587 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6590 // An existing track has been found with the requested name
6591 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6592 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6596 // Update track name and refresh GUI parts
6597 vik_track_set_name (trk, newname);
6599 // Update any subwindows that could be displaying this track which has changed name
6600 // Only one Track Edit Window
6601 if ( l->current_tp_track == trk && l->tpwin ) {
6602 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6604 // Property Dialog of the track
6605 vik_trw_layer_propwin_update ( trk );
6607 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6608 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6610 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6617 static gboolean is_valid_geocache_name ( gchar *str )
6619 gint len = strlen ( str );
6620 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]));
6623 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6625 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6626 a_acquire_set_filter_track ( trk );
6629 #ifdef VIK_CONFIG_GOOGLE
6630 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6632 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6633 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6636 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6638 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6640 gchar *escaped = uri_escape ( tr->comment );
6641 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6642 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6649 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6650 /* viewpoint is now available instead */
6651 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6653 static gpointer pass_along[8];
6655 gboolean rv = FALSE;
6658 pass_along[1] = vlp;
6659 pass_along[2] = GINT_TO_POINTER (subtype);
6660 pass_along[3] = sublayer;
6661 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6662 pass_along[5] = vvp;
6663 pass_along[6] = iter;
6664 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6666 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6670 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6673 gtk_widget_show ( item );
6675 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6676 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6677 if (tr && tr->property_dialog)
6678 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6680 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6681 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6682 if (tr && tr->property_dialog)
6683 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6686 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6687 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6688 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6689 gtk_widget_show ( item );
6691 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6693 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6694 gtk_widget_show ( item );
6696 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6697 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6698 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6699 gtk_widget_show ( item );
6701 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6703 gboolean separator_created = FALSE;
6705 /* could be a right-click using the tool */
6706 if ( vlp != NULL ) {
6707 item = gtk_menu_item_new ();
6708 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6709 gtk_widget_show ( item );
6711 separator_created = TRUE;
6713 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6717 gtk_widget_show ( item );
6720 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6722 if ( wp && wp->name ) {
6723 if ( is_valid_geocache_name ( wp->name ) ) {
6725 if ( !separator_created ) {
6726 item = gtk_menu_item_new ();
6727 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6728 gtk_widget_show ( item );
6729 separator_created = TRUE;
6732 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6734 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6735 gtk_widget_show ( item );
6739 if ( wp && wp->image )
6741 if ( !separator_created ) {
6742 item = gtk_menu_item_new ();
6743 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6744 gtk_widget_show ( item );
6745 separator_created = TRUE;
6748 // Set up image paramater
6749 pass_along[5] = wp->image;
6751 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6752 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
6753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6754 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6755 gtk_widget_show ( item );
6757 #ifdef VIK_CONFIG_GEOTAG
6758 GtkWidget *geotag_submenu = gtk_menu_new ();
6759 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6761 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6762 gtk_widget_show ( item );
6763 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6765 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6766 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6767 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6768 gtk_widget_show ( item );
6770 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6771 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6772 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6773 gtk_widget_show ( item );
6779 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6780 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6781 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6782 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6784 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6785 gtk_widget_show ( item );
6792 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6793 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6795 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6796 gtk_widget_show ( item );
6797 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6798 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6799 gtk_widget_set_sensitive ( item, TRUE );
6801 gtk_widget_set_sensitive ( item, FALSE );
6804 item = gtk_menu_item_new ();
6805 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6806 gtk_widget_show ( item );
6809 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6812 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6815 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6816 gtk_widget_show ( item );
6819 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6821 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6822 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6823 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6824 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6825 gtk_widget_show ( item );
6827 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6828 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6830 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6831 gtk_widget_show ( item );
6833 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6834 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6836 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6837 gtk_widget_show ( item );
6839 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6840 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6842 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6843 gtk_widget_show ( item );
6845 GtkWidget *vis_submenu = gtk_menu_new ();
6846 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6847 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6848 gtk_widget_show ( item );
6849 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6851 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
6852 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
6854 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6855 gtk_widget_show ( item );
6857 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
6858 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6859 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
6860 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6861 gtk_widget_show ( item );
6863 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6864 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6865 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
6866 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6867 gtk_widget_show ( item );
6870 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6874 if ( l->current_track && !l->current_track->is_route ) {
6875 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6877 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6878 gtk_widget_show ( item );
6880 item = gtk_menu_item_new ();
6881 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6882 gtk_widget_show ( item );
6885 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6886 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6887 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6888 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6889 gtk_widget_show ( item );
6891 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6892 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6894 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6895 gtk_widget_show ( item );
6896 // Make it available only when a new track *not* already in progress
6897 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6899 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6902 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6903 gtk_widget_show ( item );
6905 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6908 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6909 gtk_widget_show ( item );
6911 GtkWidget *vis_submenu = gtk_menu_new ();
6912 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6913 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6914 gtk_widget_show ( item );
6915 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6917 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
6918 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
6920 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6921 gtk_widget_show ( item );
6923 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
6924 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6925 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
6926 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6927 gtk_widget_show ( item );
6929 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6930 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6931 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
6932 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6933 gtk_widget_show ( item );
6935 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
6937 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6938 gtk_widget_show ( item );
6941 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6945 if ( l->current_track && l->current_track->is_route ) {
6946 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6947 // Reuse finish track method
6948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6949 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6950 gtk_widget_show ( item );
6952 item = gtk_menu_item_new ();
6953 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6954 gtk_widget_show ( item );
6957 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6958 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6961 gtk_widget_show ( item );
6963 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6964 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6966 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6967 gtk_widget_show ( item );
6968 // Make it available only when a new track *not* already in progress
6969 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6971 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6972 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6974 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6975 gtk_widget_show ( item );
6977 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6980 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6981 gtk_widget_show ( item );
6983 GtkWidget *vis_submenu = gtk_menu_new ();
6984 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6985 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6986 gtk_widget_show ( item );
6987 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6989 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
6990 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
6992 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6993 gtk_widget_show ( item );
6995 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
6996 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
6998 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6999 gtk_widget_show ( item );
7001 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7002 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7004 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7005 gtk_widget_show ( item );
7007 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7009 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7010 gtk_widget_show ( item );
7014 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7015 GtkWidget *submenu_sort = gtk_menu_new ();
7016 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7017 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7018 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7019 gtk_widget_show ( item );
7020 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7022 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7023 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7024 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7025 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7026 gtk_widget_show ( item );
7028 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7029 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7031 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7032 gtk_widget_show ( item );
7035 GtkWidget *upload_submenu = gtk_menu_new ();
7037 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7039 item = gtk_menu_item_new ();
7040 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7041 gtk_widget_show ( item );
7043 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7044 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7045 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7046 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7047 if ( l->current_track ) {
7048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7049 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7050 gtk_widget_show ( item );
7053 item = gtk_menu_item_new ();
7054 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7055 gtk_widget_show ( item );
7058 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7059 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7061 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7062 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7063 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7064 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7065 gtk_widget_show ( item );
7067 GtkWidget *goto_submenu;
7068 goto_submenu = gtk_menu_new ();
7069 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7071 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7072 gtk_widget_show ( item );
7073 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7075 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7076 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7078 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7079 gtk_widget_show ( item );
7081 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7082 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7083 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7084 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7085 gtk_widget_show ( item );
7087 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7090 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7091 gtk_widget_show ( item );
7093 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7095 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7096 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7097 gtk_widget_show ( item );
7099 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7102 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7103 gtk_widget_show ( item );
7105 // Routes don't have speeds
7106 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7107 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7108 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7110 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7111 gtk_widget_show ( item );
7114 GtkWidget *combine_submenu;
7115 combine_submenu = gtk_menu_new ();
7116 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7117 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7118 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7119 gtk_widget_show ( item );
7120 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7122 // Routes don't have times or segments...
7123 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7124 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7126 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7127 gtk_widget_show ( item );
7129 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7131 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7132 gtk_widget_show ( item );
7135 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7137 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7138 gtk_widget_show ( item );
7140 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7141 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7143 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7145 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7146 gtk_widget_show ( item );
7148 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7149 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7151 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7153 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7154 gtk_widget_show ( item );
7156 GtkWidget *split_submenu;
7157 split_submenu = gtk_menu_new ();
7158 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7159 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7160 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7161 gtk_widget_show ( item );
7162 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7164 // Routes don't have times or segments...
7165 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7166 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7167 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7168 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7169 gtk_widget_show ( item );
7171 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7172 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7174 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7175 gtk_widget_show ( item );
7178 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7180 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7181 gtk_widget_show ( item );
7183 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7184 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7185 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7186 gtk_widget_show ( item );
7187 // Make it available only when a trackpoint is selected.
7188 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7190 GtkWidget *delete_submenu;
7191 delete_submenu = gtk_menu_new ();
7192 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7193 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7194 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7195 gtk_widget_show ( item );
7196 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7198 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7199 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7200 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7201 gtk_widget_show ( item );
7203 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7205 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7206 gtk_widget_show ( item );
7208 GtkWidget *transform_submenu;
7209 transform_submenu = gtk_menu_new ();
7210 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7212 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7213 gtk_widget_show ( item );
7214 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7216 GtkWidget *dem_submenu;
7217 dem_submenu = gtk_menu_new ();
7218 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7219 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
7220 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7221 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7223 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7225 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7226 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7227 gtk_widget_show ( item );
7229 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7231 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7232 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7233 gtk_widget_show ( item );
7235 GtkWidget *smooth_submenu;
7236 smooth_submenu = gtk_menu_new ();
7237 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7238 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7239 gtk_widget_show ( item );
7240 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7242 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7244 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7245 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7246 gtk_widget_show ( item );
7248 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7250 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7251 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7252 gtk_widget_show ( item );
7254 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7255 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7257 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7258 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7259 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7260 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7261 gtk_widget_show ( item );
7263 // Routes don't have timestamps - so this is only available for tracks
7264 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7265 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7266 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7267 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7268 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7269 gtk_widget_show ( item );
7272 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7273 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7275 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7276 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7277 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7278 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7279 gtk_widget_show ( item );
7281 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7282 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7283 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7285 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7286 gtk_widget_show ( item );
7289 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7291 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7292 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7294 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7295 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
7296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7297 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7298 gtk_widget_show ( item );
7301 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7302 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7304 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7307 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7308 gtk_widget_show ( item );
7310 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7311 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7313 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7314 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7316 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7317 gtk_widget_show ( item );
7319 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7320 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7321 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
7322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7323 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7324 gtk_widget_show ( item );
7327 // ATM can't upload a single waypoint but can do waypoints to a GPS
7328 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7329 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7330 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7331 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7332 gtk_widget_show ( item );
7333 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7335 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7336 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7337 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7338 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7339 gtk_widget_show ( item );
7343 #ifdef VIK_CONFIG_GOOGLE
7344 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7346 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7348 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7349 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7350 gtk_widget_show ( item );
7354 // Some things aren't usable with routes
7355 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7356 #ifdef VIK_CONFIG_OPENSTREETMAP
7357 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7358 // Convert internal pointer into actual track for usage outside this file
7359 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7360 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7362 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7363 gtk_widget_show ( item );
7366 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7367 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7369 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7370 gtk_widget_show ( item );
7372 /* ATM This function is only available via the layers panel, due to needing a vlp */
7374 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7375 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7376 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7378 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7379 gtk_widget_show ( item );
7383 #ifdef VIK_CONFIG_GEOTAG
7384 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7386 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7387 gtk_widget_show ( item );
7391 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7392 // Only show on viewport popmenu when a trackpoint is selected
7393 if ( ! vlp && l->current_tpl ) {
7395 item = gtk_menu_item_new ();
7396 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7397 gtk_widget_show ( item );
7399 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7400 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7401 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7402 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7403 gtk_widget_show ( item );
7407 gtk_widget_show_all ( GTK_WIDGET(menu) );
7412 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7415 if (!vtl->current_tpl)
7417 if (!vtl->current_tpl->next)
7420 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7421 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7423 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7426 VikTrackpoint *tp_new = vik_trackpoint_new();
7427 struct LatLon ll_current, ll_next;
7428 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7429 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7431 /* main positional interpolation */
7432 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7433 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7435 /* Now other properties that can be interpolated */
7436 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7438 if (tp_current->has_timestamp && tp_next->has_timestamp) {
7439 /* Note here the division is applied to each part, then added
7440 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7441 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7442 tp_new->has_timestamp = TRUE;
7445 if (tp_current->speed != NAN && tp_next->speed != NAN)
7446 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7448 /* TODO - improve interpolation of course, as it may not be correct.
7449 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7450 [similar applies if value is in radians] */
7451 if (tp_current->course != NAN && tp_next->course != NAN)
7452 tp_new->course = (tp_current->course + tp_next->course)/2;
7454 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7456 /* Insert new point into the trackpoints list after the current TP */
7457 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7459 // Otherwise try routes
7460 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7464 gint index = g_list_index ( trk->trackpoints, tp_current );
7466 // NB no recalculation of bounds since it is inserted between points
7467 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7472 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7478 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7482 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7484 if ( vtl->current_tpl )
7486 vtl->current_tpl = NULL;
7487 vtl->current_tp_track = NULL;
7488 vtl->current_tp_id = NULL;
7489 vik_layer_emit_update(VIK_LAYER(vtl));
7493 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7495 g_assert ( vtl->tpwin != NULL );
7496 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7497 trw_layer_cancel_current_tp ( vtl, TRUE );
7499 if ( vtl->current_tpl == NULL )
7502 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7504 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7505 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7507 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7509 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7511 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7517 // Find available adjacent trackpoint
7518 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7520 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7521 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7523 // Delete current trackpoint
7524 vik_trackpoint_free ( vtl->current_tpl->data );
7525 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7527 // Set to current to the available adjacent trackpoint
7528 vtl->current_tpl = new_tpl;
7530 // Reset dialog with the available adjacent trackpoint
7531 if ( vtl->current_tp_track ) {
7532 vik_track_calculate_bounds ( vtl->current_tp_track );
7533 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7536 vik_layer_emit_update(VIK_LAYER(vtl));
7540 // Delete current trackpoint
7541 vik_trackpoint_free ( vtl->current_tpl->data );
7542 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7543 trw_layer_cancel_current_tp ( vtl, FALSE );
7546 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7548 if ( vtl->current_tp_track )
7549 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7550 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7552 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7554 if ( vtl->current_tp_track )
7555 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7556 vik_layer_emit_update(VIK_LAYER(vtl));
7558 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7560 trw_layer_insert_tp_after_current_tp ( vtl );
7561 vik_layer_emit_update(VIK_LAYER(vtl));
7563 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7564 vik_layer_emit_update(VIK_LAYER(vtl));
7568 * trw_layer_dialog_shift:
7569 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7571 * Try to reposition a dialog if it's over the specified coord
7572 * so to not obscure the item of interest
7574 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7576 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
7578 // Attempt force dialog to be shown so we can find out where it is more reliably...
7579 while ( gtk_events_pending() )
7580 gtk_main_iteration ();
7582 // get parent window position & size
7583 gint win_pos_x, win_pos_y;
7584 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
7586 gint win_size_x, win_size_y;
7587 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
7589 // get own dialog size
7590 gint dia_size_x, dia_size_y;
7591 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
7593 // get own dialog position
7594 gint dia_pos_x, dia_pos_y;
7595 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
7597 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
7598 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
7600 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
7602 gint vp_xx, vp_yy; // In viewport pixels
7603 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
7605 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
7609 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
7611 // Transform Viewport pixels into absolute pixels
7612 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
7613 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
7615 // Is dialog over the point (to within an ^^ edge value)
7616 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
7617 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
7621 gint hh = vik_viewport_get_height ( vvp );
7623 // Consider the difference in viewport to the full window
7624 gint offset_y = dest_y;
7625 // Add difference between dialog and window sizes
7626 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
7628 if ( vp_yy > hh/2 ) {
7629 // Point in bottom half, move window to top half
7630 gtk_window_move ( dialog, dia_pos_x, offset_y );
7633 // Point in top half, move dialog down
7634 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
7638 // Shift left<->right
7639 gint ww = vik_viewport_get_width ( vvp );
7641 // Consider the difference in viewport to the full window
7642 gint offset_x = dest_x;
7643 // Add difference between dialog and window sizes
7644 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
7646 if ( vp_xx > ww/2 ) {
7647 // Point on right, move window to left
7648 gtk_window_move ( dialog, offset_x, dia_pos_y );
7651 // Point on left, move right
7652 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7660 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7664 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7665 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7666 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7667 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7669 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7671 if ( vtl->current_tpl ) {
7672 // get tp pixel position
7673 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7675 // Shift up<->down to try not to obscure the trackpoint.
7676 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7680 if ( vtl->current_tpl )
7681 if ( vtl->current_tp_track )
7682 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7683 /* set layer name and TP data */
7686 /***************************************************************************
7688 ***************************************************************************/
7690 /*** Utility data structures and functions ****/
7694 gint closest_x, closest_y;
7695 gboolean draw_images;
7696 gpointer *closest_wp_id;
7697 VikWaypoint *closest_wp;
7703 gint closest_x, closest_y;
7704 gpointer closest_track_id;
7705 VikTrackpoint *closest_tp;
7711 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7717 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7719 // If waypoint has an image then use the image size to select
7720 if ( params->draw_images && wp->image ) {
7721 gint slackx, slacky;
7722 slackx = wp->image_width / 2;
7723 slacky = wp->image_height / 2;
7725 if ( x <= params->x + slackx && x >= params->x - slackx
7726 && y <= params->y + slacky && y >= params->y - slacky ) {
7727 params->closest_wp_id = id;
7728 params->closest_wp = wp;
7729 params->closest_x = x;
7730 params->closest_y = y;
7733 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7734 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7735 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7737 params->closest_wp_id = id;
7738 params->closest_wp = wp;
7739 params->closest_x = x;
7740 params->closest_y = y;
7744 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7746 GList *tpl = t->trackpoints;
7752 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7758 tp = VIK_TRACKPOINT(tpl->data);
7760 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7762 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7763 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7764 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7766 params->closest_track_id = id;
7767 params->closest_tp = tp;
7768 params->closest_tpl = tpl;
7769 params->closest_x = x;
7770 params->closest_y = y;
7776 // ATM: Leave this as 'Track' only.
7777 // Not overly bothered about having a snap to route trackpoint capability
7778 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7780 TPSearchParams params;
7784 params.closest_track_id = NULL;
7785 params.closest_tp = NULL;
7786 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7787 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7788 return params.closest_tp;
7791 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7793 WPSearchParams params;
7797 params.draw_images = vtl->drawimages;
7798 params.closest_wp = NULL;
7799 params.closest_wp_id = NULL;
7800 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7801 return params.closest_wp;
7805 // Some forward declarations
7806 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7807 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7808 static void marker_end_move ( tool_ed_t *t );
7811 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7815 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7817 // Here always allow snapping back to the original location
7818 // this is useful when one decides not to move the thing afterall
7819 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7822 if ( event->state & GDK_CONTROL_MASK )
7824 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7826 new_coord = tp->coord;
7830 if ( event->state & GDK_SHIFT_MASK )
7832 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7834 new_coord = wp->coord;
7838 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7840 marker_moveto ( t, x, y );
7847 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7849 if ( t->holding && event->button == 1 )
7852 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7855 if ( event->state & GDK_CONTROL_MASK )
7857 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7859 new_coord = tp->coord;
7863 if ( event->state & GDK_SHIFT_MASK )
7865 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7867 new_coord = wp->coord;
7870 marker_end_move ( t );
7872 // Determine if working on a waypoint or a trackpoint
7873 if ( t->is_waypoint ) {
7874 vtl->current_wp->coord = new_coord;
7875 trw_layer_calculate_bounds_waypoints ( vtl );
7878 if ( vtl->current_tpl ) {
7879 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7881 if ( vtl->current_tp_track )
7882 vik_track_calculate_bounds ( vtl->current_tp_track );
7885 if ( vtl->current_tp_track )
7886 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7891 vtl->current_wp = NULL;
7892 vtl->current_wp_id = NULL;
7893 trw_layer_cancel_current_tp ( vtl, FALSE );
7895 vik_layer_emit_update ( VIK_LAYER(vtl) );
7902 Returns true if a waypoint or track is found near the requested event position for this particular layer
7903 The item found is automatically selected
7904 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7906 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7908 if ( event->button != 1 )
7911 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7914 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7918 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7920 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7922 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7923 WPSearchParams wp_params;
7924 wp_params.vvp = vvp;
7925 wp_params.x = event->x;
7926 wp_params.y = event->y;
7927 wp_params.draw_images = vtl->drawimages;
7928 wp_params.closest_wp_id = NULL;
7929 wp_params.closest_wp = NULL;
7931 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7933 if ( wp_params.closest_wp ) {
7936 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7938 // Too easy to move it so must be holding shift to start immediately moving it
7939 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7940 if ( event->state & GDK_SHIFT_MASK ||
7941 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7942 // Put into 'move buffer'
7943 // NB vvp & vw already set in tet
7944 tet->vtl = (gpointer)vtl;
7945 tet->is_waypoint = TRUE;
7947 marker_begin_move (tet, event->x, event->y);
7950 vtl->current_wp = wp_params.closest_wp;
7951 vtl->current_wp_id = wp_params.closest_wp_id;
7953 vik_layer_emit_update ( VIK_LAYER(vtl) );
7959 // Used for both track and route lists
7960 TPSearchParams tp_params;
7961 tp_params.vvp = vvp;
7962 tp_params.x = event->x;
7963 tp_params.y = event->y;
7964 tp_params.closest_track_id = NULL;
7965 tp_params.closest_tp = NULL;
7966 tp_params.closest_tpl = NULL;
7967 tp_params.bbox = bbox;
7969 if (vtl->tracks_visible) {
7970 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7972 if ( tp_params.closest_tp ) {
7974 // Always select + highlight the track
7975 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7977 tet->is_waypoint = FALSE;
7979 // Select the Trackpoint
7980 // Can move it immediately when control held or it's the previously selected tp
7981 if ( event->state & GDK_CONTROL_MASK ||
7982 vtl->current_tpl == tp_params.closest_tpl ) {
7983 // Put into 'move buffer'
7984 // NB vvp & vw already set in tet
7985 tet->vtl = (gpointer)vtl;
7986 marker_begin_move (tet, event->x, event->y);
7989 vtl->current_tpl = tp_params.closest_tpl;
7990 vtl->current_tp_id = tp_params.closest_track_id;
7991 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7993 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7996 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7998 vik_layer_emit_update ( VIK_LAYER(vtl) );
8003 // Try again for routes
8004 if (vtl->routes_visible) {
8005 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8007 if ( tp_params.closest_tp ) {
8009 // Always select + highlight the track
8010 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8012 tet->is_waypoint = FALSE;
8014 // Select the Trackpoint
8015 // Can move it immediately when control held or it's the previously selected tp
8016 if ( event->state & GDK_CONTROL_MASK ||
8017 vtl->current_tpl == tp_params.closest_tpl ) {
8018 // Put into 'move buffer'
8019 // NB vvp & vw already set in tet
8020 tet->vtl = (gpointer)vtl;
8021 marker_begin_move (tet, event->x, event->y);
8024 vtl->current_tpl = tp_params.closest_tpl;
8025 vtl->current_tp_id = tp_params.closest_track_id;
8026 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8028 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8031 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8033 vik_layer_emit_update ( VIK_LAYER(vtl) );
8038 /* these aren't the droids you're looking for */
8039 vtl->current_wp = NULL;
8040 vtl->current_wp_id = NULL;
8041 trw_layer_cancel_current_tp ( vtl, FALSE );
8044 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8049 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8051 if ( event->button != 3 )
8054 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8057 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8060 /* Post menu for the currently selected item */
8062 /* See if a track is selected */
8063 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8064 if ( track && track->visible ) {
8066 if ( track->name ) {
8068 if ( vtl->track_right_click_menu )
8069 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8071 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8078 if ( track->is_route )
8079 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8081 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8083 if ( trkf && udataU.uuid ) {
8086 if ( track->is_route )
8087 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8089 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8091 trw_layer_sublayer_add_menu_items ( vtl,
8092 vtl->track_right_click_menu,
8094 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8100 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8106 /* See if a waypoint is selected */
8107 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8108 if ( waypoint && waypoint->visible ) {
8109 if ( waypoint->name ) {
8111 if ( vtl->wp_right_click_menu )
8112 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8114 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8117 udata.wp = waypoint;
8120 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8122 if ( wpf && udata.uuid ) {
8123 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8125 trw_layer_sublayer_add_menu_items ( vtl,
8126 vtl->wp_right_click_menu,
8128 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8133 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8142 /* background drawing hook, to be passed the viewport */
8143 static gboolean tool_sync_done = TRUE;
8145 static gboolean tool_sync(gpointer data)
8147 VikViewport *vvp = data;
8148 gdk_threads_enter();
8149 vik_viewport_sync(vvp);
8150 tool_sync_done = TRUE;
8151 gdk_threads_leave();
8155 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8158 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8159 gdk_gc_set_function ( t->gc, GDK_INVERT );
8160 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8161 vik_viewport_sync(t->vvp);
8166 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8168 VikViewport *vvp = t->vvp;
8169 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8170 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8174 if (tool_sync_done) {
8175 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8176 tool_sync_done = FALSE;
8180 static void marker_end_move ( tool_ed_t *t )
8182 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8183 g_object_unref ( t->gc );
8187 /*** Edit waypoint ****/
8189 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8191 tool_ed_t *t = g_new(tool_ed_t, 1);
8197 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8202 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8204 WPSearchParams params;
8205 tool_ed_t *t = data;
8206 VikViewport *vvp = t->vvp;
8208 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8215 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8218 if ( vtl->current_wp && vtl->current_wp->visible )
8220 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8222 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8224 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8225 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8227 if ( event->button == 3 )
8228 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8230 marker_begin_move(t, event->x, event->y);
8237 params.x = event->x;
8238 params.y = event->y;
8239 params.draw_images = vtl->drawimages;
8240 params.closest_wp_id = NULL;
8241 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8242 params.closest_wp = NULL;
8243 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8244 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8246 // how do we get here?
8247 marker_begin_move(t, event->x, event->y);
8248 g_critical("shouldn't be here");
8251 else if ( params.closest_wp )
8253 if ( event->button == 3 )
8254 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8256 vtl->waypoint_rightclick = FALSE;
8258 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8260 vtl->current_wp = params.closest_wp;
8261 vtl->current_wp_id = params.closest_wp_id;
8263 /* could make it so don't update if old WP is off screen and new is null but oh well */
8264 vik_layer_emit_update ( VIK_LAYER(vtl) );
8268 vtl->current_wp = NULL;
8269 vtl->current_wp_id = NULL;
8270 vtl->waypoint_rightclick = FALSE;
8271 vik_layer_emit_update ( VIK_LAYER(vtl) );
8275 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8277 tool_ed_t *t = data;
8278 VikViewport *vvp = t->vvp;
8280 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8285 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8288 if ( event->state & GDK_CONTROL_MASK )
8290 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8292 new_coord = tp->coord;
8296 if ( event->state & GDK_SHIFT_MASK )
8298 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8299 if ( wp && wp != vtl->current_wp )
8300 new_coord = wp->coord;
8305 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8307 marker_moveto ( t, x, y );
8314 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8316 tool_ed_t *t = data;
8317 VikViewport *vvp = t->vvp;
8319 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8322 if ( t->holding && event->button == 1 )
8325 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8328 if ( event->state & GDK_CONTROL_MASK )
8330 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8332 new_coord = tp->coord;
8336 if ( event->state & GDK_SHIFT_MASK )
8338 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8339 if ( wp && wp != vtl->current_wp )
8340 new_coord = wp->coord;
8343 marker_end_move ( t );
8345 vtl->current_wp->coord = new_coord;
8347 trw_layer_calculate_bounds_waypoints ( vtl );
8348 vik_layer_emit_update ( VIK_LAYER(vtl) );
8351 /* PUT IN RIGHT PLACE!!! */
8352 if ( event->button == 3 && vtl->waypoint_rightclick )
8354 if ( vtl->wp_right_click_menu )
8355 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8356 if ( vtl->current_wp ) {
8357 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8358 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 );
8359 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8361 vtl->waypoint_rightclick = FALSE;
8366 /*** New track ****/
8368 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8375 GdkDrawable *drawable;
8381 * Draw specified pixmap
8383 static gboolean draw_sync ( gpointer data )
8385 draw_sync_t *ds = (draw_sync_t*) data;
8386 // Sometimes don't want to draw
8387 // normally because another update has taken precedent such as panning the display
8388 // which means this pixmap is no longer valid
8389 if ( ds->vtl->draw_sync_do ) {
8390 gdk_threads_enter();
8391 gdk_draw_drawable (ds->drawable,
8394 0, 0, 0, 0, -1, -1);
8395 ds->vtl->draw_sync_done = TRUE;
8396 gdk_threads_leave();
8402 static gchar* distance_string (gdouble distance)
8406 /* draw label with distance */
8407 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8408 switch (dist_units) {
8409 case VIK_UNITS_DISTANCE_MILES:
8410 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8411 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8412 } else if (distance < 1609.4) {
8413 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8415 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8419 // VIK_UNITS_DISTANCE_KILOMETRES
8420 if (distance >= 1000 && distance < 100000) {
8421 g_sprintf(str, "%3.2f km", distance/1000.0);
8422 } else if (distance < 1000) {
8423 g_sprintf(str, "%d m", (int)distance);
8425 g_sprintf(str, "%d km", (int)distance/1000);
8429 return g_strdup (str);
8433 * Actually set the message in statusbar
8435 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8437 // Only show elevation data when track has some elevation properties
8438 gchar str_gain_loss[64];
8439 str_gain_loss[0] = '\0';
8440 gchar str_last_step[64];
8441 str_last_step[0] = '\0';
8442 gchar *str_total = distance_string (distance);
8444 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8445 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8446 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8448 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8451 if ( last_step > 0 ) {
8452 gchar *tmp = distance_string (last_step);
8453 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8457 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8459 // Write with full gain/loss information
8460 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8461 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8463 g_free ( str_total );
8467 * Figure out what information should be set in the statusbar and then write it
8469 static void update_statusbar ( VikTrwLayer *vtl )
8471 // Get elevation data
8472 gdouble elev_gain, elev_loss;
8473 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8475 /* Find out actual distance of current track */
8476 gdouble distance = vik_track_get_length (vtl->current_track);
8478 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8482 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8484 /* if we haven't sync'ed yet, we don't have time to do more. */
8485 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8486 GList *iter = g_list_last ( vtl->current_track->trackpoints );
8487 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8489 static GdkPixmap *pixmap = NULL;
8491 // Need to check in case window has been resized
8492 w1 = vik_viewport_get_width(vvp);
8493 h1 = vik_viewport_get_height(vvp);
8495 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8497 gdk_drawable_get_size (pixmap, &w2, &h2);
8498 if (w1 != w2 || h1 != h2) {
8499 g_object_unref ( G_OBJECT ( pixmap ) );
8500 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8503 // Reset to background
8504 gdk_draw_drawable (pixmap,
8505 vtl->current_track_newpoint_gc,
8506 vik_viewport_get_pixmap(vvp),
8507 0, 0, 0, 0, -1, -1);
8509 draw_sync_t *passalong;
8512 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8514 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8515 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8516 // thus when we come to reset to the background it would include what we have already drawn!!
8517 gdk_draw_line ( pixmap,
8518 vtl->current_track_newpoint_gc,
8519 x1, y1, event->x, event->y );
8520 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8522 /* Find out actual distance of current track */
8523 gdouble distance = vik_track_get_length (vtl->current_track);
8525 // Now add distance to where the pointer is //
8528 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8529 vik_coord_to_latlon ( &coord, &ll );
8530 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8531 distance = distance + last_step;
8533 // Get elevation data
8534 gdouble elev_gain, elev_loss;
8535 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8537 // Adjust elevation data (if available) for the current pointer position
8539 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8540 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8541 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8542 // Adjust elevation of last track point
8543 if ( elev_new > last_tpt->altitude )
8545 elev_gain += elev_new - last_tpt->altitude;
8548 elev_loss += last_tpt->altitude - elev_new;
8553 // Display of the distance 'tooltip' during track creation is controlled by a preference
8555 if ( a_vik_get_create_track_tooltip() ) {
8557 gchar *str = distance_string (distance);
8559 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8560 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8561 pango_layout_set_text (pl, str, -1);
8563 pango_layout_get_pixel_size ( pl, &wd, &hd );
8566 // offset from cursor a bit depending on font size
8570 // Create a background block to make the text easier to read over the background map
8571 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8572 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8573 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8575 g_object_unref ( G_OBJECT ( pl ) );
8576 g_object_unref ( G_OBJECT ( background_block_gc ) );
8580 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
8581 passalong->vtl = vtl;
8582 passalong->pixmap = pixmap;
8583 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
8584 passalong->gc = vtl->current_track_newpoint_gc;
8588 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
8590 // Update statusbar with full gain/loss information
8591 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
8593 // draw pixmap when we have time to
8594 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
8595 vtl->draw_sync_done = FALSE;
8596 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
8598 return VIK_LAYER_TOOL_ACK;
8601 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
8603 if ( vtl->current_track && event->keyval == GDK_Escape ) {
8604 vtl->current_track = NULL;
8605 vik_layer_emit_update ( VIK_LAYER(vtl) );
8607 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
8609 if ( vtl->current_track->trackpoints )
8611 GList *last = g_list_last(vtl->current_track->trackpoints);
8612 g_free ( last->data );
8613 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8616 update_statusbar ( vtl );
8618 vik_layer_emit_update ( VIK_LAYER(vtl) );
8625 * Common function to handle trackpoint button requests on either a route or a track
8626 * . enables adding a point via normal click
8627 * . enables removal of last point via right click
8628 * . finishing of the track or route via double clicking
8630 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8634 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8637 if ( event->button == 2 ) {
8638 // As the display is panning, the new track pixmap is now invalid so don't draw it
8639 // otherwise this drawing done results in flickering back to an old image
8640 vtl->draw_sync_do = FALSE;
8644 if ( event->button == 3 )
8646 if ( !vtl->current_track )
8649 if ( vtl->current_track->trackpoints )
8651 GList *last = g_list_last(vtl->current_track->trackpoints);
8652 g_free ( last->data );
8653 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8655 vik_track_calculate_bounds ( vtl->current_track );
8656 update_statusbar ( vtl );
8658 vik_layer_emit_update ( VIK_LAYER(vtl) );
8662 if ( event->type == GDK_2BUTTON_PRESS )
8664 /* subtract last (duplicate from double click) tp then end */
8665 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8667 GList *last = g_list_last(vtl->current_track->trackpoints);
8668 g_free ( last->data );
8669 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8670 /* undo last, then end */
8671 vtl->current_track = NULL;
8673 vik_layer_emit_update ( VIK_LAYER(vtl) );
8677 tp = vik_trackpoint_new();
8678 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8680 /* snap to other TP */
8681 if ( event->state & GDK_CONTROL_MASK )
8683 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8685 tp->coord = other_tp->coord;
8688 tp->newsegment = FALSE;
8689 tp->has_timestamp = FALSE;
8692 if ( vtl->current_track ) {
8693 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
8694 /* Auto attempt to get elevation from DEM data (if it's available) */
8695 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8698 vtl->ct_x1 = vtl->ct_x2;
8699 vtl->ct_y1 = vtl->ct_y2;
8700 vtl->ct_x2 = event->x;
8701 vtl->ct_y2 = event->y;
8703 vik_layer_emit_update ( VIK_LAYER(vtl) );
8707 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8709 // ----------------------------------------------------- if current is a route - switch to new track
8710 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8712 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8713 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8715 new_track_create_common ( vtl, name );
8721 return tool_new_track_or_route_click ( vtl, event, vvp );
8724 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8726 if ( event->button == 2 ) {
8727 // Pan moving ended - enable potential point drawing again
8728 vtl->draw_sync_do = TRUE;
8729 vtl->draw_sync_done = TRUE;
8733 /*** New route ****/
8735 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8740 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8742 // -------------------------- if current is a track - switch to new route
8743 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8745 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8746 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) ) {
8747 new_route_create_common ( vtl, name );
8753 return tool_new_track_or_route_click ( vtl, event, vvp );
8756 /*** New waypoint ****/
8758 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8763 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8766 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8768 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8769 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8770 trw_layer_calculate_bounds_waypoints ( vtl );
8771 vik_layer_emit_update ( VIK_LAYER(vtl) );
8777 /*** Edit trackpoint ****/
8779 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8781 tool_ed_t *t = g_new(tool_ed_t, 1);
8787 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
8792 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8794 tool_ed_t *t = data;
8795 VikViewport *vvp = t->vvp;
8796 TPSearchParams params;
8797 /* OUTDATED DOCUMENTATION:
8798 find 5 pixel range on each side. then put these UTM, and a pointer
8799 to the winning track name (and maybe the winning track itself), and a
8800 pointer to the winning trackpoint, inside an array or struct. pass
8801 this along, do a foreach on the tracks which will do a foreach on the
8804 params.x = event->x;
8805 params.y = event->y;
8806 params.closest_track_id = NULL;
8807 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8808 params.closest_tp = NULL;
8809 params.closest_tpl = NULL;
8810 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8812 if ( event->button != 1 )
8815 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8818 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8821 if ( vtl->current_tpl )
8823 /* 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.) */
8824 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8825 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8830 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8832 if ( current_tr->visible &&
8833 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8834 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8835 marker_begin_move ( t, event->x, event->y );
8841 if ( vtl->tracks_visible )
8842 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8844 if ( params.closest_tp )
8846 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8847 vtl->current_tpl = params.closest_tpl;
8848 vtl->current_tp_id = params.closest_track_id;
8849 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8850 trw_layer_tpwin_init ( vtl );
8851 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8852 vik_layer_emit_update ( VIK_LAYER(vtl) );
8856 if ( vtl->routes_visible )
8857 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8859 if ( params.closest_tp )
8861 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8862 vtl->current_tpl = params.closest_tpl;
8863 vtl->current_tp_id = params.closest_track_id;
8864 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8865 trw_layer_tpwin_init ( vtl );
8866 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8867 vik_layer_emit_update ( VIK_LAYER(vtl) );
8871 /* these aren't the droids you're looking for */
8875 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8877 tool_ed_t *t = data;
8878 VikViewport *vvp = t->vvp;
8880 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8886 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8889 if ( event->state & GDK_CONTROL_MASK )
8891 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8892 if ( tp && tp != vtl->current_tpl->data )
8893 new_coord = tp->coord;
8895 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8898 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8899 marker_moveto ( t, x, y );
8907 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8909 tool_ed_t *t = data;
8910 VikViewport *vvp = t->vvp;
8912 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8914 if ( event->button != 1)
8919 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8922 if ( event->state & GDK_CONTROL_MASK )
8924 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8925 if ( tp && tp != vtl->current_tpl->data )
8926 new_coord = tp->coord;
8929 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8930 if ( vtl->current_tp_track )
8931 vik_track_calculate_bounds ( vtl->current_tp_track );
8933 marker_end_move ( t );
8935 /* diff dist is diff from orig */
8937 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8939 vik_layer_emit_update ( VIK_LAYER(vtl) );
8946 /*** Route Finder ***/
8947 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8952 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8955 if ( !vtl ) return FALSE;
8956 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8957 if ( event->button == 3 && vtl->route_finder_current_track ) {
8959 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8961 vtl->route_finder_coord = *new_end;
8963 vik_layer_emit_update ( VIK_LAYER(vtl) );
8964 /* remove last ' to:...' */
8965 if ( vtl->route_finder_current_track->comment ) {
8966 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8967 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8968 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8969 last_to - vtl->route_finder_current_track->comment - 1);
8970 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8975 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8976 struct LatLon start, end;
8978 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8979 vik_coord_to_latlon ( &(tmp), &end );
8980 vtl->route_finder_coord = tmp; /* for continuations */
8982 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8983 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8984 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8986 vtl->route_finder_check_added_track = TRUE;
8987 vtl->route_finder_started = FALSE;
8990 vik_routing_default_find ( vtl, start, end);
8992 /* see if anything was done -- a track was added or appended to */
8993 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8994 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 ) );
8995 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8996 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8997 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8998 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9001 if ( vtl->route_finder_added_track )
9002 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9004 vtl->route_finder_added_track = NULL;
9005 vtl->route_finder_check_added_track = FALSE;
9006 vtl->route_finder_append = FALSE;
9008 vik_layer_emit_update ( VIK_LAYER(vtl) );
9010 vtl->route_finder_started = TRUE;
9011 vtl->route_finder_coord = tmp;
9012 vtl->route_finder_current_track = NULL;
9017 /*** Show picture ****/
9019 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9024 /* Params are: vvp, event, last match found or NULL */
9025 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9027 if ( wp->image && wp->visible )
9029 gint x, y, slackx, slacky;
9030 GdkEventButton *event = (GdkEventButton *) params[1];
9032 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9033 slackx = wp->image_width / 2;
9034 slacky = wp->image_height / 2;
9035 if ( x <= event->x + slackx && x >= event->x - slackx
9036 && y <= event->y + slacky && y >= event->y - slacky )
9038 params[2] = wp->image; /* we've found a match. however continue searching
9039 * since we want to find the last match -- that
9040 * is, the match that was drawn last. */
9045 static void trw_layer_show_picture ( gpointer pass_along[6] )
9047 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9049 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9052 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9053 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9054 g_free ( quoted_file );
9055 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9057 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() );
9058 g_error_free ( err );
9061 #endif /* WINDOWS */
9064 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9066 gpointer params[3] = { vvp, event, NULL };
9067 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9069 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9072 static gpointer pass_along[6];
9073 pass_along[0] = vtl;
9074 pass_along[5] = params[2];
9075 trw_layer_show_picture ( pass_along );
9076 return TRUE; /* found a match */
9079 return FALSE; /* go through other layers, searching for a match */
9082 /***************************************************************************
9084 ***************************************************************************/
9087 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9089 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9090 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9093 /* Structure for thumbnail creating data used in the background thread */
9095 VikTrwLayer *vtl; // Layer needed for redrawing
9096 GSList *pics; // Image list
9097 } thumbnail_create_thread_data;
9099 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9101 guint total = g_slist_length(tctd->pics), done = 0;
9102 while ( tctd->pics )
9104 a_thumbnails_create ( (gchar *) tctd->pics->data );
9105 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9107 return -1; /* Abort thread */
9109 tctd->pics = tctd->pics->next;
9112 // Redraw to show the thumbnails as they are now created
9113 if ( IS_VIK_LAYER(tctd->vtl) )
9114 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9119 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9121 while ( tctd->pics )
9123 g_free ( tctd->pics->data );
9124 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9129 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9131 if ( ! vtl->has_verified_thumbnails )
9133 GSList *pics = NULL;
9134 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9137 gint len = g_slist_length ( pics );
9138 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9139 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9142 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9144 (vik_thr_func) create_thumbnails_thread,
9146 (vik_thr_free_func) thumbnail_create_thread_free,
9154 static const gchar* my_track_colors ( gint ii )
9156 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9168 // Fast and reliable way of returning a colour
9169 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9172 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9174 GHashTableIter iter;
9175 gpointer key, value;
9179 g_hash_table_iter_init ( &iter, vtl->tracks );
9181 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9183 // Tracks get a random spread of colours if not already assigned
9184 if ( ! VIK_TRACK(value)->has_color ) {
9185 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9186 VIK_TRACK(value)->color = vtl->track_color;
9188 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9190 VIK_TRACK(value)->has_color = TRUE;
9193 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9196 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9202 g_hash_table_iter_init ( &iter, vtl->routes );
9204 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9206 // Routes get an intermix of reds
9207 if ( ! VIK_TRACK(value)->has_color ) {
9209 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9211 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9212 VIK_TRACK(value)->has_color = TRUE;
9215 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9222 * (Re)Calculate the bounds of the waypoints in this layer,
9223 * This should be called whenever waypoints are changed
9225 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9227 struct LatLon topleft = { 0.0, 0.0 };
9228 struct LatLon bottomright = { 0.0, 0.0 };
9231 GHashTableIter iter;
9232 gpointer key, value;
9234 g_hash_table_iter_init ( &iter, vtl->waypoints );
9236 // Set bounds to first point
9237 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9238 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9239 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9242 // Ensure there is another point...
9243 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9245 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9247 // See if this point increases the bounds.
9248 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9250 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9251 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9252 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9253 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9257 vtl->waypoints_bbox.north = topleft.lat;
9258 vtl->waypoints_bbox.east = bottomright.lon;
9259 vtl->waypoints_bbox.south = bottomright.lat;
9260 vtl->waypoints_bbox.west = topleft.lon;
9263 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9265 vik_track_calculate_bounds ( trk );
9268 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9270 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9271 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9274 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9276 if ( ! VIK_LAYER(vtl)->vt )
9279 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9280 if ( g_hash_table_size (vtl->tracks) > 1 )
9281 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9283 if ( g_hash_table_size (vtl->routes) > 1 )
9284 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9286 if ( g_hash_table_size (vtl->waypoints) > 1 )
9287 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9290 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9292 trw_layer_verify_thumbnails ( vtl, vvp );
9293 trw_layer_track_alloc_colors ( vtl );
9295 trw_layer_calculate_bounds_waypoints ( vtl );
9296 trw_layer_calculate_bounds_tracks ( vtl );
9298 // Apply treeview sort after loading all the tracks for this layer
9299 // (rather than sorted insert on each individual track additional)
9300 // and after subsequent changes to the properties as the specified order may have changed.
9301 // since the sorting of a treeview section is now very quick
9302 // NB sorting is also performed after every name change as well to maintain the list order
9303 trw_layer_sort_all ( vtl );
9306 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9308 return vtl->coord_mode;
9312 * Uniquify the whole layer
9313 * Also requires the layers panel as the names shown there need updating too
9314 * Returns whether the operation was successful or not
9316 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9319 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9320 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9321 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9327 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9329 vik_coord_convert ( &(wp->coord), *dest_mode );
9332 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9334 vik_track_convert ( tr, *dest_mode );
9337 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9339 if ( vtl->coord_mode != dest_mode )
9341 vtl->coord_mode = dest_mode;
9342 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9343 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9344 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9348 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9350 vtl->menu_selection = selection;
9353 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9355 return (vtl->menu_selection);
9358 /* ----------- Downloading maps along tracks --------------- */
9360 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9362 /* TODO: calculating based on current size of viewport */
9363 const gdouble w_at_zoom_0_125 = 0.0013;
9364 const gdouble h_at_zoom_0_125 = 0.0011;
9365 gdouble zoom_factor = zoom_level/0.125;
9367 wh->lat = h_at_zoom_0_125 * zoom_factor;
9368 wh->lon = w_at_zoom_0_125 * zoom_factor;
9370 return 0; /* all OK */
9373 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9375 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9376 (dist->lat >= ABS(to->north_south - from->north_south)))
9379 VikCoord *coord = g_malloc(sizeof(VikCoord));
9380 coord->mode = VIK_COORD_LATLON;
9382 if (ABS(gradient) < 1) {
9383 if (from->east_west > to->east_west)
9384 coord->east_west = from->east_west - dist->lon;
9386 coord->east_west = from->east_west + dist->lon;
9387 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9389 if (from->north_south > to->north_south)
9390 coord->north_south = from->north_south - dist->lat;
9392 coord->north_south = from->north_south + dist->lat;
9393 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9399 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9401 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9402 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9404 VikCoord *next = from;
9406 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9408 list = g_list_prepend(list, next);
9414 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9416 typedef struct _Rect {
9421 #define GLRECT(iter) ((Rect *)((iter)->data))
9424 GList *rects_to_download = NULL;
9427 if (get_download_area_width(vvp, zoom_level, &wh))
9430 GList *iter = tr->trackpoints;
9434 gboolean new_map = TRUE;
9435 VikCoord *cur_coord, tl, br;
9438 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9440 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9441 rect = g_malloc(sizeof(Rect));
9444 rect->center = *cur_coord;
9445 rects_to_download = g_list_prepend(rects_to_download, rect);
9450 gboolean found = FALSE;
9451 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9452 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9463 GList *fillins = NULL;
9464 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9465 /* seems that ATM the function get_next_coord works only for LATLON */
9466 if ( cur_coord->mode == VIK_COORD_LATLON ) {
9467 /* fill-ins for far apart points */
9468 GList *cur_rect, *next_rect;
9469 for (cur_rect = rects_to_download;
9470 (next_rect = cur_rect->next) != NULL;
9471 cur_rect = cur_rect->next) {
9472 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9473 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9474 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9478 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9481 GList *iter = fillins;
9483 cur_coord = (VikCoord *)(iter->data);
9484 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9485 rect = g_malloc(sizeof(Rect));
9488 rect->center = *cur_coord;
9489 rects_to_download = g_list_prepend(rects_to_download, rect);
9494 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9495 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9499 for (iter = fillins; iter; iter = iter->next)
9501 g_list_free(fillins);
9503 if (rects_to_download) {
9504 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9505 g_free(rect_iter->data);
9506 g_list_free(rects_to_download);
9510 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9513 gint selected_map, default_map;
9514 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9515 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9516 gint selected_zoom, default_zoom;
9520 VikTrwLayer *vtl = pass_along[0];
9521 VikLayersPanel *vlp = pass_along[1];
9523 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9524 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9526 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9530 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9532 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9533 int num_maps = g_list_length(vmls);
9536 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
9540 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9541 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9543 gchar **np = map_names;
9544 VikMapsLayer **lp = map_layers;
9545 for (i = 0; i < num_maps; i++) {
9546 gboolean dup = FALSE;
9547 vml = (VikMapsLayer *)(vmls->data);
9548 for (j = 0; j < i; j++) { /* no duplicate allowed */
9549 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
9556 *np++ = vik_maps_layer_get_map_label(vml);
9562 num_maps = lp - map_layers;
9564 for (default_map = 0; default_map < num_maps; default_map++) {
9565 /* TODO: check for parent layer's visibility */
9566 if (VIK_LAYER(map_layers[default_map])->visible)
9569 default_map = (default_map == num_maps) ? 0 : default_map;
9571 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9572 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
9573 if (cur_zoom == zoom_vals[default_zoom])
9576 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
9578 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
9581 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9584 for (i = 0; i < num_maps; i++)
9585 g_free(map_names[i]);
9593 /**** lowest waypoint number calculation ***/
9594 static gint highest_wp_number_name_to_number(const gchar *name) {
9595 if ( strlen(name) == 3 ) {
9597 if ( n < 100 && name[0] != '0' )
9599 if ( n < 10 && name[0] != '0' )
9607 static void highest_wp_number_reset(VikTrwLayer *vtl)
9609 vtl->highest_wp_number = -1;
9612 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
9614 /* if is bigger that top, add it */
9615 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
9616 if ( new_wp_num > vtl->highest_wp_number )
9617 vtl->highest_wp_number = new_wp_num;
9620 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
9622 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
9623 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
9624 if ( vtl->highest_wp_number == old_wp_num ) {
9626 vtl->highest_wp_number--;
9628 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9629 /* search down until we find something that *does* exist */
9631 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
9632 vtl->highest_wp_number--;
9633 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9638 /* get lowest unused number */
9639 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
9642 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
9644 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
9645 return g_strdup(buf);