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) ;
4607 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4609 // If have a vlp then perform a basic test to see if any DEM info available...
4611 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4613 if ( !g_list_length(dems) ) {
4614 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
4622 * apply_dem_data_common:
4624 * A common function for applying the DEM values and reporting the results.
4626 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
4628 if ( !trw_layer_dem_test ( vtl, vlp ) )
4631 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
4632 // Inform user how much was changed
4634 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4635 g_snprintf(str, 64, tmp_str, changed);
4636 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4639 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
4641 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4643 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4644 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4646 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4649 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
4652 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
4654 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4656 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4657 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4659 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4662 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
4668 * A common function for applying the elevation smoothing and reporting the results.
4670 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
4672 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
4673 // Inform user how much was changed
4675 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4676 g_snprintf(str, 64, tmp_str, changed);
4677 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4683 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
4685 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4687 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4688 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4690 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4695 smooth_it ( vtl, track, FALSE );
4698 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
4700 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4702 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4703 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4705 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4710 smooth_it ( vtl, track, TRUE );
4714 * Commonal helper function
4716 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
4719 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
4720 g_snprintf(str, 64, tmp_str, changed);
4721 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4724 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
4726 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4727 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
4729 if ( !trw_layer_dem_test ( vtl, vlp ) )
4733 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
4735 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4737 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
4741 GHashTableIter iter;
4742 gpointer key, value;
4744 g_hash_table_iter_init ( &iter, vtl->waypoints );
4745 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
4746 VikWaypoint *wp = VIK_WAYPOINT(value);
4747 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
4750 wp_changed_message ( vtl, changed );
4753 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
4755 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4756 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
4758 if ( !trw_layer_dem_test ( vtl, vlp ) )
4762 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
4764 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4766 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
4770 GHashTableIter iter;
4771 gpointer key, value;
4773 g_hash_table_iter_init ( &iter, vtl->waypoints );
4774 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
4775 VikWaypoint *wp = VIK_WAYPOINT(value);
4776 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
4779 wp_changed_message ( vtl, changed );
4782 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4784 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4786 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4787 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4789 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4794 GList *trps = track->trackpoints;
4797 trps = g_list_last(trps);
4798 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4801 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4803 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4805 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4806 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4808 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4813 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4816 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4819 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4821 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4823 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4824 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4826 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4831 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4834 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4837 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4839 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4841 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4842 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4844 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4849 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4852 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4856 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4858 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4860 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4862 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4863 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4865 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4867 if ( trk && trk->trackpoints )
4869 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4870 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4871 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
4872 if ( pass_along[1] )
4873 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4875 vik_layer_emit_update ( VIK_LAYER(vtl) );
4880 * Refine the selected track/route with a routing engine.
4881 * The routing engine is selected by the user, when requestiong the job.
4883 static void trw_layer_route_refine ( gpointer pass_along[6] )
4885 static gint last_engine = 0;
4886 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4889 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4890 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4892 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4894 if ( trk && trk->trackpoints )
4896 /* Check size of the route */
4897 int nb = vik_track_get_tp_count(trk);
4899 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
4900 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4901 GTK_MESSAGE_WARNING,
4902 GTK_BUTTONS_OK_CANCEL,
4903 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
4905 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
4906 gtk_widget_destroy ( dialog );
4907 if (response != GTK_RESPONSE_OK )
4910 /* Select engine from dialog */
4911 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
4912 VIK_GTK_WINDOW_FROM_LAYER (vtl),
4913 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4915 GTK_RESPONSE_REJECT,
4917 GTK_RESPONSE_ACCEPT,
4919 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
4920 gtk_widget_show_all(label);
4922 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
4924 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
4925 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
4926 gtk_widget_show_all(combo);
4928 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
4930 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4932 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4934 /* Dialog validated: retrieve selected engine and do the job */
4935 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
4936 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
4939 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
4941 /* Force saving track */
4942 /* FIXME: remove or rename this hack */
4943 vtl->route_finder_check_added_track = TRUE;
4946 vik_routing_engine_refine (routing, vtl, trk);
4948 /* FIXME: remove or rename this hack */
4949 if ( vtl->route_finder_added_track )
4950 vik_track_calculate_bounds ( vtl->route_finder_added_track );
4952 vtl->route_finder_added_track = NULL;
4953 vtl->route_finder_check_added_track = FALSE;
4955 vik_layer_emit_update ( VIK_LAYER(vtl) );
4957 /* Restore cursor */
4958 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
4960 gtk_widget_destroy ( dialog );
4964 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4966 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4967 trw_layer_tpwin_init ( vtl );
4970 /*************************************
4971 * merge/split by time routines
4972 *************************************/
4974 /* called for each key in track hash table.
4975 * If the current track has the same time stamp type, add it to the result,
4976 * except the one pointed by "exclude".
4977 * set exclude to NULL if there is no exclude to check.
4978 * Note that the result is in reverse (for performance reasons).
4983 gboolean with_timestamps;
4985 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4987 twt_udata *user_data = udata;
4988 VikTrackpoint *p1, *p2;
4990 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4994 if (VIK_TRACK(value)->trackpoints) {
4995 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4996 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4998 if ( user_data->with_timestamps ) {
4999 if (!p1->has_timestamp || !p2->has_timestamp) {
5004 // Don't add tracks with timestamps when getting non timestamp tracks
5005 if (p1->has_timestamp || p2->has_timestamp) {
5011 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5014 /* called for each key in track hash table. if original track user_data[1] is close enough
5015 * to the passed one, add it to list in user_data[0]
5017 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5020 VikTrackpoint *p1, *p2;
5021 VikTrack *trk = VIK_TRACK(value);
5023 GList **nearby_tracks = ((gpointer *)user_data)[0];
5024 GList *tpoints = ((gpointer *)user_data)[1];
5027 * detect reasons for not merging, and return
5028 * if no reason is found not to merge, then do it.
5031 // Exclude the original track from the compiled list
5032 if (trk->trackpoints == tpoints) {
5036 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5037 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5039 if (trk->trackpoints) {
5040 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5041 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5043 if (!p1->has_timestamp || !p2->has_timestamp) {
5044 //g_print("no timestamp\n");
5048 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5049 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5050 if (! (abs(t1 - p2->timestamp) < threshold ||
5052 abs(p1->timestamp - t2) < threshold)
5059 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5062 /* comparison function used to sort tracks; a and b are hash table keys */
5063 /* Not actively used - can be restored if needed
5064 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5066 GHashTable *tracks = user_data;
5069 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5070 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5072 if (t1 < t2) return -1;
5073 if (t1 > t2) return 1;
5078 /* comparison function used to sort trackpoints */
5079 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5081 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5083 if (t1 < t2) return -1;
5084 if (t1 > t2) return 1;
5089 * comparison function which can be used to sort tracks or waypoints by name
5091 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5093 const gchar* namea = (const gchar*) a;
5094 const gchar* nameb = (const gchar*) b;
5095 if ( namea == NULL || nameb == NULL)
5098 // Same sort method as used in the vik_treeview_*_alphabetize functions
5099 return strcmp ( namea, nameb );
5103 * Attempt to merge selected track with other tracks specified by the user
5104 * Tracks to merge with must be of the same 'type' as the selected track -
5105 * either all with timestamps, or all without timestamps
5107 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5109 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5110 GList *other_tracks = NULL;
5111 GHashTable *ght_tracks;
5112 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5113 ght_tracks = vtl->routes;
5115 ght_tracks = vtl->tracks;
5117 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5122 if ( !track->trackpoints )
5126 udata.result = &other_tracks;
5127 udata.exclude = track->trackpoints;
5128 // Allow merging with 'similar' time type time tracks
5129 // i.e. either those times, or those without
5130 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5132 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5133 other_tracks = g_list_reverse(other_tracks);
5135 if ( !other_tracks ) {
5136 if ( udata.with_timestamps )
5137 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5139 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5143 // Sort alphabetically for user presentation
5144 // Convert into list of names for usage with dialog function
5145 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5146 GList *other_tracks_names = NULL;
5147 GList *iter = g_list_first ( other_tracks );
5149 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5150 iter = g_list_next ( iter );
5153 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5155 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5159 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5160 g_list_free(other_tracks);
5161 g_list_free(other_tracks_names);
5166 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5167 VikTrack *merge_track;
5168 if ( track->is_route )
5169 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5171 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5174 vik_track_steal_and_append_trackpoints ( track, merge_track );
5175 if ( track->is_route )
5176 vik_trw_layer_delete_route (vtl, merge_track);
5178 vik_trw_layer_delete_track (vtl, merge_track);
5179 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5182 for (l = merge_list; l != NULL; l = g_list_next(l))
5184 g_list_free(merge_list);
5186 vik_layer_emit_update( VIK_LAYER(vtl) );
5190 // c.f. trw_layer_sorted_track_id_by_name_list
5191 // but don't add the specified track to the list (normally current track)
5192 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5194 twt_udata *user_data = udata;
5197 if (trk->trackpoints == user_data->exclude) {
5201 // Sort named list alphabetically
5202 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5206 * Join - this allows combining 'tracks' and 'track routes'
5207 * i.e. doesn't care about whether tracks have consistent timestamps
5208 * ATM can only append one track at a time to the currently selected track
5210 static void trw_layer_append_track ( gpointer pass_along[6] )
5213 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5215 GHashTable *ght_tracks;
5216 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5217 ght_tracks = vtl->routes;
5219 ght_tracks = vtl->tracks;
5221 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5226 GList *other_tracks_names = NULL;
5228 // Sort alphabetically for user presentation
5229 // Convert into list of names for usage with dialog function
5230 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5232 udata.result = &other_tracks_names;
5233 udata.exclude = trk->trackpoints;
5235 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5237 // Note the limit to selecting one track only
5238 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5239 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5240 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5243 trk->is_route ? _("Append Route"): _("Append Track"),
5244 trk->is_route ? _("Select the route to append after the current route") :
5245 _("Select the track to append after the current track") );
5247 g_list_free(other_tracks_names);
5249 // It's a list, but shouldn't contain more than one other track!
5250 if ( append_list ) {
5252 for (l = append_list; l != NULL; l = g_list_next(l)) {
5253 // TODO: at present this uses the first track found by name,
5254 // which with potential multiple same named tracks may not be the one selected...
5255 VikTrack *append_track;
5256 if ( trk->is_route )
5257 append_track = vik_trw_layer_get_route ( vtl, l->data );
5259 append_track = vik_trw_layer_get_track ( vtl, l->data );
5261 if ( append_track ) {
5262 vik_track_steal_and_append_trackpoints ( trk, append_track );
5263 if ( trk->is_route )
5264 vik_trw_layer_delete_route (vtl, append_track);
5266 vik_trw_layer_delete_track (vtl, append_track);
5269 for (l = append_list; l != NULL; l = g_list_next(l))
5271 g_list_free(append_list);
5273 vik_layer_emit_update( VIK_LAYER(vtl) );
5278 * Very similar to trw_layer_append_track for joining
5279 * but this allows selection from the 'other' list
5280 * If a track is selected, then is shows routes and joins the selected one
5281 * If a route is selected, then is shows tracks and joins the selected one
5283 static void trw_layer_append_other ( gpointer pass_along[6] )
5286 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5288 GHashTable *ght_mykind, *ght_others;
5289 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5290 ght_mykind = vtl->routes;
5291 ght_others = vtl->tracks;
5294 ght_mykind = vtl->tracks;
5295 ght_others = vtl->routes;
5298 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5303 GList *other_tracks_names = NULL;
5305 // Sort alphabetically for user presentation
5306 // Convert into list of names for usage with dialog function
5307 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5309 udata.result = &other_tracks_names;
5310 udata.exclude = trk->trackpoints;
5312 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5314 // Note the limit to selecting one track only
5315 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5316 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5317 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5320 trk->is_route ? _("Append Track"): _("Append Route"),
5321 trk->is_route ? _("Select the track to append after the current route") :
5322 _("Select the route to append after the current track") );
5324 g_list_free(other_tracks_names);
5326 // It's a list, but shouldn't contain more than one other track!
5327 if ( append_list ) {
5329 for (l = append_list; l != NULL; l = g_list_next(l)) {
5330 // TODO: at present this uses the first track found by name,
5331 // which with potential multiple same named tracks may not be the one selected...
5333 // Get FROM THE OTHER TYPE list
5334 VikTrack *append_track;
5335 if ( trk->is_route )
5336 append_track = vik_trw_layer_get_track ( vtl, l->data );
5338 append_track = vik_trw_layer_get_route ( vtl, l->data );
5340 if ( append_track ) {
5342 if ( !append_track->is_route &&
5343 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5344 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5346 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5347 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5348 vik_track_merge_segments ( append_track );
5349 vik_track_to_routepoints ( append_track );
5356 vik_track_steal_and_append_trackpoints ( trk, append_track );
5358 // Delete copied which is FROM THE OTHER TYPE list
5359 if ( trk->is_route )
5360 vik_trw_layer_delete_track (vtl, append_track);
5362 vik_trw_layer_delete_route (vtl, append_track);
5365 for (l = append_list; l != NULL; l = g_list_next(l))
5367 g_list_free(append_list);
5368 vik_layer_emit_update( VIK_LAYER(vtl) );
5372 /* merge by segments */
5373 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5375 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5376 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5377 guint segments = vik_track_merge_segments ( trk );
5378 // NB currently no need to redraw as segments not actually shown on the display
5379 // However inform the user of what happened:
5381 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5382 g_snprintf(str, 64, tmp_str, segments);
5383 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5386 /* merge by time routine */
5387 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5389 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5393 GList *tracks_with_timestamp = NULL;
5394 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5395 if (orig_trk->trackpoints &&
5396 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5397 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5402 udata.result = &tracks_with_timestamp;
5403 udata.exclude = orig_trk->trackpoints;
5404 udata.with_timestamps = TRUE;
5405 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5406 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5408 if (!tracks_with_timestamp) {
5409 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5412 g_list_free(tracks_with_timestamp);
5414 static guint threshold_in_minutes = 1;
5415 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5416 _("Merge Threshold..."),
5417 _("Merge when time between tracks less than:"),
5418 &threshold_in_minutes)) {
5422 // keep attempting to merge all tracks until no merges within the time specified is possible
5423 gboolean attempt_merge = TRUE;
5424 GList *nearby_tracks = NULL;
5426 static gpointer params[3];
5428 while ( attempt_merge ) {
5430 // Don't try again unless tracks have changed
5431 attempt_merge = FALSE;
5433 trps = orig_trk->trackpoints;
5437 if (nearby_tracks) {
5438 g_list_free(nearby_tracks);
5439 nearby_tracks = NULL;
5442 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5443 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5445 /* g_print("Original track times: %d and %d\n", t1, t2); */
5446 params[0] = &nearby_tracks;
5447 params[1] = (gpointer)trps;
5448 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5450 /* get a list of adjacent-in-time tracks */
5451 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5454 GList *l = nearby_tracks;
5457 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5458 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5460 t1 = get_first_trackpoint(l)->timestamp;
5461 t2 = get_last_trackpoint(l)->timestamp;
5462 #undef get_first_trackpoint
5463 #undef get_last_trackpoint
5464 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5467 /* remove trackpoints from merged track, delete track */
5468 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5469 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5471 // Tracks have changed, therefore retry again against all the remaining tracks
5472 attempt_merge = TRUE;
5477 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5480 g_list_free(nearby_tracks);
5482 vik_layer_emit_update( VIK_LAYER(vtl) );
5486 * Split a track at the currently selected trackpoint
5488 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5490 if ( !vtl->current_tpl )
5493 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5494 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5496 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5497 GList *newglist = g_list_alloc ();
5498 newglist->prev = NULL;
5499 newglist->next = vtl->current_tpl->next;
5500 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5501 tr->trackpoints = newglist;
5503 vtl->current_tpl->next->prev = newglist; /* end old track here */
5504 vtl->current_tpl->next = NULL;
5506 // Bounds of the selected track changed due to the split
5507 vik_track_calculate_bounds ( vtl->current_tp_track );
5509 vtl->current_tpl = newglist; /* change tp to first of new track. */
5510 vtl->current_tp_track = tr;
5513 vik_trw_layer_add_route ( vtl, name, tr );
5515 vik_trw_layer_add_track ( vtl, name, tr );
5517 // Bounds of the new track created by the split
5518 vik_track_calculate_bounds ( tr );
5524 // Also need id of newly created track
5527 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5529 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5531 if ( trkf && udata.uuid )
5532 vtl->current_tp_id = udata.uuid;
5534 vtl->current_tp_id = NULL;
5536 vik_layer_emit_update(VIK_LAYER(vtl));
5542 /* split by time routine */
5543 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5545 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5546 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5547 GList *trps = track->trackpoints;
5549 GList *newlists = NULL;
5550 GList *newtps = NULL;
5551 static guint thr = 1;
5558 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5559 _("Split Threshold..."),
5560 _("Split when time between trackpoints exceeds:"),
5565 /* iterate through trackpoints, and copy them into new lists without touching original list */
5566 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5570 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5572 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5575 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5576 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5577 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5579 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5584 if (ts - prev_ts > thr*60) {
5585 /* flush accumulated trackpoints into new list */
5586 newlists = g_list_append(newlists, g_list_reverse(newtps));
5590 /* accumulate trackpoint copies in newtps, in reverse order */
5591 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5593 iter = g_list_next(iter);
5596 newlists = g_list_append(newlists, g_list_reverse(newtps));
5599 /* put lists of trackpoints into tracks */
5601 // Only bother updating if the split results in new tracks
5602 if (g_list_length (newlists) > 1) {
5607 tr = vik_track_copy ( track, FALSE );
5608 tr->trackpoints = (GList *)(iter->data);
5610 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5611 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5612 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5613 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5614 g_free ( new_tr_name );
5615 vik_track_calculate_bounds ( tr );
5616 iter = g_list_next(iter);
5618 // Remove original track and then update the display
5619 vik_trw_layer_delete_track (vtl, track);
5620 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5622 g_list_free(newlists);
5626 * Split a track by the number of points as specified by the user
5628 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5630 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5632 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5633 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5635 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5640 // Check valid track
5641 GList *trps = track->trackpoints;
5645 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5646 _("Split Every Nth Point"),
5647 _("Split on every Nth point:"),
5648 250, // Default value as per typical limited track capacity of various GPS devices
5652 // Was a valid number returned?
5658 GList *newlists = NULL;
5659 GList *newtps = NULL;
5664 /* accumulate trackpoint copies in newtps, in reverse order */
5665 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5667 if (count >= points) {
5668 /* flush accumulated trackpoints into new list */
5669 newlists = g_list_append(newlists, g_list_reverse(newtps));
5673 iter = g_list_next(iter);
5676 // If there is a remaining chunk put that into the new split list
5677 // This may well be the whole track if no split points were encountered
5679 newlists = g_list_append(newlists, g_list_reverse(newtps));
5682 /* put lists of trackpoints into tracks */
5684 // Only bother updating if the split results in new tracks
5685 if (g_list_length (newlists) > 1) {
5690 tr = vik_track_copy ( track, FALSE );
5691 tr->trackpoints = (GList *)(iter->data);
5693 if ( track->is_route ) {
5694 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5695 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5698 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5699 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5701 g_free ( new_tr_name );
5702 vik_track_calculate_bounds ( tr );
5704 iter = g_list_next(iter);
5706 // Remove original track and then update the display
5707 if ( track->is_route )
5708 vik_trw_layer_delete_route (vtl, track);
5710 vik_trw_layer_delete_track (vtl, track);
5711 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5713 g_list_free(newlists);
5717 * Split a track at the currently selected trackpoint
5719 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5721 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5722 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5723 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5727 * Split a track by its segments
5728 * Routes do not have segments so don't call this for routes
5730 static void trw_layer_split_segments ( gpointer pass_along[6] )
5732 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5733 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5740 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5743 for ( i = 0; i < ntracks; i++ ) {
5745 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5746 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5747 g_free ( new_tr_name );
5752 // Remove original track
5753 vik_trw_layer_delete_track ( vtl, trk );
5754 vik_layer_emit_update ( VIK_LAYER(vtl) );
5757 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5760 /* end of split/merge routines */
5763 * Delete adjacent track points at the same position
5764 * AKA Delete Dulplicates on the Properties Window
5766 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5768 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5770 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5771 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5773 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5778 gulong removed = vik_track_remove_dup_points ( trk );
5780 // Track has been updated so update tps:
5781 trw_layer_cancel_tps_of_track ( vtl, trk );
5783 // Inform user how much was deleted as it's not obvious from the normal view
5785 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5786 g_snprintf(str, 64, tmp_str, removed);
5787 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5789 vik_layer_emit_update ( VIK_LAYER(vtl) );
5793 * Delete adjacent track points with the same timestamp
5794 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5796 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5798 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5800 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5801 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5803 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5808 gulong removed = vik_track_remove_same_time_points ( trk );
5810 // Track has been updated so update tps:
5811 trw_layer_cancel_tps_of_track ( vtl, trk );
5813 // Inform user how much was deleted as it's not obvious from the normal view
5815 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5816 g_snprintf(str, 64, tmp_str, removed);
5817 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5819 vik_layer_emit_update ( VIK_LAYER(vtl) );
5825 static void trw_layer_reverse ( gpointer pass_along[6] )
5827 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5829 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5830 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5832 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5837 vik_track_reverse ( track );
5839 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5843 * Similar to trw_layer_enum_item, but this uses a sorted method
5846 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5848 GList **list = (GList**)udata;
5849 // *list = g_list_prepend(*all, key); //unsorted method
5850 // Sort named list alphabetically
5851 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5856 * Now Waypoint specific sort
5858 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5860 GList **list = (GList**)udata;
5861 // Sort named list alphabetically
5862 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5866 * Track specific sort
5868 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5870 GList **list = (GList**)udata;
5871 // Sort named list alphabetically
5872 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5877 gboolean has_same_track_name;
5878 const gchar *same_track_name;
5879 } same_track_name_udata;
5881 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5883 const gchar* namea = (const gchar*) aa;
5884 const gchar* nameb = (const gchar*) bb;
5887 gint result = strcmp ( namea, nameb );
5889 if ( result == 0 ) {
5890 // Found two names the same
5891 same_track_name_udata *user_data = udata;
5892 user_data->has_same_track_name = TRUE;
5893 user_data->same_track_name = namea;
5896 // Leave ordering the same
5901 * Find out if any tracks have the same name in this hash table
5903 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5905 // Sort items by name, then compare if any next to each other are the same
5907 GList *track_names = NULL;
5908 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5911 if ( ! track_names )
5914 same_track_name_udata udata;
5915 udata.has_same_track_name = FALSE;
5917 // Use sort routine to traverse list comparing items
5918 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5919 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5920 // Still no tracks...
5924 return udata.has_same_track_name;
5928 * Force unqiue track names for the track table specified
5929 * Note the panel is a required parameter to enable the update of the names displayed
5930 * Specify if on tracks or else on routes
5932 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5934 // . Search list for an instance of repeated name
5935 // . get track of this name
5936 // . create new name
5937 // . rename track & update equiv. treeview iter
5938 // . repeat until all different
5940 same_track_name_udata udata;
5942 GList *track_names = NULL;
5943 udata.has_same_track_name = FALSE;
5944 udata.same_track_name = NULL;
5946 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5949 if ( ! track_names )
5952 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5954 // Still no tracks...
5955 if ( ! dummy_list1 )
5958 while ( udata.has_same_track_name ) {
5960 // Find a track with the same name
5963 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5965 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5969 g_critical("Houston, we've had a problem.");
5970 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5971 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5976 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5977 vik_track_set_name ( trk, newname );
5983 // Need want key of it for treeview update
5984 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5986 if ( trkf && udataU.uuid ) {
5990 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5992 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5995 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5997 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
5999 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6003 // Start trying to find same names again...
6005 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6006 udata.has_same_track_name = FALSE;
6007 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6009 // No tracks any more - give up searching
6010 if ( ! dummy_list2 )
6011 udata.has_same_track_name = FALSE;
6015 vik_layers_panel_emit_update ( vlp );
6018 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6020 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6023 switch (GPOINTER_TO_INT (pass_along[2])) {
6024 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6025 iter = &(vtl->tracks_iter);
6026 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6028 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6029 iter = &(vtl->routes_iter);
6030 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6032 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6033 iter = &(vtl->waypoints_iter);
6034 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6038 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6041 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6043 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6046 switch (GPOINTER_TO_INT (pass_along[2])) {
6047 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6048 iter = &(vtl->tracks_iter);
6049 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6051 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6052 iter = &(vtl->routes_iter);
6053 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6055 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6056 iter = &(vtl->waypoints_iter);
6057 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6061 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6067 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6069 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6072 // Ensure list of track names offered is unique
6073 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6074 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6075 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6076 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6082 // Sort list alphabetically for better presentation
6083 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6086 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6090 // Get list of items to delete from the user
6091 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6094 _("Delete Selection"),
6095 _("Select tracks to delete"));
6098 // Delete requested tracks
6099 // since specificly requested, IMHO no need for extra confirmation
6100 if ( delete_list ) {
6102 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6103 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6104 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6106 g_list_free(delete_list);
6107 vik_layer_emit_update( VIK_LAYER(vtl) );
6114 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6116 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6119 // Ensure list of track names offered is unique
6120 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6121 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6122 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6123 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6129 // Sort list alphabetically for better presentation
6130 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6133 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6137 // Get list of items to delete from the user
6138 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6141 _("Delete Selection"),
6142 _("Select routes to delete") );
6145 // Delete requested routes
6146 // since specificly requested, IMHO no need for extra confirmation
6147 if ( delete_list ) {
6149 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6150 // This deletes first route it finds of that name (but uniqueness is enforced above)
6151 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6153 g_list_free(delete_list);
6154 vik_layer_emit_update( VIK_LAYER(vtl) );
6159 gboolean has_same_waypoint_name;
6160 const gchar *same_waypoint_name;
6161 } same_waypoint_name_udata;
6163 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6165 const gchar* namea = (const gchar*) aa;
6166 const gchar* nameb = (const gchar*) bb;
6169 gint result = strcmp ( namea, nameb );
6171 if ( result == 0 ) {
6172 // Found two names the same
6173 same_waypoint_name_udata *user_data = udata;
6174 user_data->has_same_waypoint_name = TRUE;
6175 user_data->same_waypoint_name = namea;
6178 // Leave ordering the same
6183 * Find out if any waypoints have the same name in this layer
6185 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6187 // Sort items by name, then compare if any next to each other are the same
6189 GList *waypoint_names = NULL;
6190 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6193 if ( ! waypoint_names )
6196 same_waypoint_name_udata udata;
6197 udata.has_same_waypoint_name = FALSE;
6199 // Use sort routine to traverse list comparing items
6200 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6201 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6202 // Still no waypoints...
6206 return udata.has_same_waypoint_name;
6210 * Force unqiue waypoint names for this layer
6211 * Note the panel is a required parameter to enable the update of the names displayed
6213 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6215 // . Search list for an instance of repeated name
6216 // . get waypoint of this name
6217 // . create new name
6218 // . rename waypoint & update equiv. treeview iter
6219 // . repeat until all different
6221 same_waypoint_name_udata udata;
6223 GList *waypoint_names = NULL;
6224 udata.has_same_waypoint_name = FALSE;
6225 udata.same_waypoint_name = NULL;
6227 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6230 if ( ! waypoint_names )
6233 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6235 // Still no waypoints...
6236 if ( ! dummy_list1 )
6239 while ( udata.has_same_waypoint_name ) {
6241 // Find a waypoint with the same name
6242 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6246 g_critical("Houston, we've had a problem.");
6247 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6248 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6253 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6255 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6257 // Start trying to find same names again...
6258 waypoint_names = NULL;
6259 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6260 udata.has_same_waypoint_name = FALSE;
6261 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6263 // No waypoints any more - give up searching
6264 if ( ! dummy_list2 )
6265 udata.has_same_waypoint_name = FALSE;
6269 vik_layers_panel_emit_update ( vlp );
6275 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6277 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6280 // Ensure list of waypoint names offered is unique
6281 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6282 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6283 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6284 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6290 // Sort list alphabetically for better presentation
6291 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6293 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6297 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6299 // Get list of items to delete from the user
6300 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6303 _("Delete Selection"),
6304 _("Select waypoints to delete"));
6307 // Delete requested waypoints
6308 // since specificly requested, IMHO no need for extra confirmation
6309 if ( delete_list ) {
6311 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6312 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6313 trw_layer_delete_waypoint_by_name (vtl, l->data);
6315 g_list_free(delete_list);
6317 trw_layer_calculate_bounds_waypoints ( vtl );
6318 vik_layer_emit_update( VIK_LAYER(vtl) );
6326 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6328 vik_treeview_item_toggle_visible ( vt, it );
6334 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6336 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6342 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6344 wp->visible = GPOINTER_TO_INT (on_off);
6350 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6352 wp->visible = !wp->visible;
6358 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6360 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6361 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6362 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6363 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6365 vik_layer_emit_update ( VIK_LAYER(vtl) );
6371 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6373 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6374 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6375 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6376 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6378 vik_layer_emit_update ( VIK_LAYER(vtl) );
6384 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6386 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6387 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6388 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6390 vik_layer_emit_update ( VIK_LAYER(vtl) );
6396 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6398 trk->visible = GPOINTER_TO_INT (on_off);
6404 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6406 trk->visible = !trk->visible;
6412 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6414 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6415 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6416 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6417 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6419 vik_layer_emit_update ( VIK_LAYER(vtl) );
6425 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6427 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6428 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6429 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6430 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6432 vik_layer_emit_update ( VIK_LAYER(vtl) );
6438 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6440 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6441 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6442 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6444 vik_layer_emit_update ( VIK_LAYER(vtl) );
6450 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6452 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6453 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6454 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6455 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6457 vik_layer_emit_update ( VIK_LAYER(vtl) );
6463 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6465 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6466 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6467 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6468 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6470 vik_layer_emit_update ( VIK_LAYER(vtl) );
6476 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6478 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6479 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6480 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6482 vik_layer_emit_update ( VIK_LAYER(vtl) );
6486 * trw_layer_analyse_close:
6488 * Stuff to do on dialog closure
6490 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6492 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6493 gtk_widget_destroy ( dialog );
6494 vtl->tracks_analysis_dialog = NULL;
6498 * trw_layer_analyse_create_list:
6500 * Create the latest list of tracks with the associated layer(s)
6501 * Although this will always be from a single layer here
6503 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6505 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6506 GList *tracks = NULL;
6507 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6508 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6510 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6512 GList *tracks_and_layers = NULL;
6513 // build tracks_and_layers list
6514 tracks = g_list_first ( tracks );
6516 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6517 vtdl->trk = VIK_TRACK(tracks->data);
6519 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6520 tracks = g_list_next ( tracks );
6523 return tracks_and_layers;
6526 static void trw_layer_tracks_stats ( gpointer lav[2] )
6528 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6529 // There can only be one!
6530 if ( vtl->tracks_analysis_dialog )
6533 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6534 VIK_LAYER(vtl)->name,
6536 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6537 trw_layer_analyse_create_list,
6538 trw_layer_analyse_close );
6544 static void trw_layer_routes_stats ( gpointer lav[2] )
6546 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6547 // There can only be one!
6548 if ( vtl->tracks_analysis_dialog )
6551 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6552 VIK_LAYER(vtl)->name,
6554 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6555 trw_layer_analyse_create_list,
6556 trw_layer_analyse_close );
6559 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6561 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6563 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6566 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6568 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6571 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6572 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6576 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6578 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6581 if ( !strncmp(wp->comment, "http", 4) ) {
6582 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6583 } else if ( !strncmp(wp->description, "http", 4) ) {
6584 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6588 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6590 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6592 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6594 // No actual change to the name supplied
6596 if (strcmp(newname, wp->name) == 0 )
6599 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6602 // An existing waypoint has been found with the requested name
6603 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6604 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6609 // Update WP name and refresh the treeview
6610 vik_waypoint_set_name (wp, newname);
6612 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6613 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6615 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6620 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6622 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6624 // No actual change to the name supplied
6626 if (strcmp(newname, trk->name) == 0)
6629 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6632 // An existing track has been found with the requested name
6633 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6634 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6638 // Update track name and refresh GUI parts
6639 vik_track_set_name (trk, newname);
6641 // Update any subwindows that could be displaying this track which has changed name
6642 // Only one Track Edit Window
6643 if ( l->current_tp_track == trk && l->tpwin ) {
6644 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6646 // Property Dialog of the track
6647 vik_trw_layer_propwin_update ( trk );
6649 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6650 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6652 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6657 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6659 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6661 // No actual change to the name supplied
6663 if (strcmp(newname, trk->name) == 0)
6666 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6669 // An existing track has been found with the requested name
6670 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6671 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6675 // Update track name and refresh GUI parts
6676 vik_track_set_name (trk, newname);
6678 // Update any subwindows that could be displaying this track which has changed name
6679 // Only one Track Edit Window
6680 if ( l->current_tp_track == trk && l->tpwin ) {
6681 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6683 // Property Dialog of the track
6684 vik_trw_layer_propwin_update ( trk );
6686 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6687 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6689 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6696 static gboolean is_valid_geocache_name ( gchar *str )
6698 gint len = strlen ( str );
6699 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]));
6702 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6704 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6705 a_acquire_set_filter_track ( trk );
6708 #ifdef VIK_CONFIG_GOOGLE
6709 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6711 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6712 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6715 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6717 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6719 gchar *escaped = uri_escape ( tr->comment );
6720 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6721 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6728 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6729 /* viewpoint is now available instead */
6730 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6732 static gpointer pass_along[8];
6734 gboolean rv = FALSE;
6737 pass_along[1] = vlp;
6738 pass_along[2] = GINT_TO_POINTER (subtype);
6739 pass_along[3] = sublayer;
6740 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6741 pass_along[5] = vvp;
6742 pass_along[6] = iter;
6743 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6745 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6749 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6750 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6751 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6752 gtk_widget_show ( item );
6754 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6755 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6756 if (tr && tr->property_dialog)
6757 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6759 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6760 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6761 if (tr && tr->property_dialog)
6762 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6765 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6766 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6768 gtk_widget_show ( item );
6770 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6771 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6772 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6773 gtk_widget_show ( item );
6775 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6777 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6778 gtk_widget_show ( item );
6780 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6782 gboolean separator_created = FALSE;
6784 /* could be a right-click using the tool */
6785 if ( vlp != NULL ) {
6786 item = gtk_menu_item_new ();
6787 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6788 gtk_widget_show ( item );
6790 separator_created = TRUE;
6792 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6793 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6795 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6796 gtk_widget_show ( item );
6799 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6801 if ( wp && wp->name ) {
6802 if ( is_valid_geocache_name ( wp->name ) ) {
6804 if ( !separator_created ) {
6805 item = gtk_menu_item_new ();
6806 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6807 gtk_widget_show ( item );
6808 separator_created = TRUE;
6811 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6812 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6813 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6814 gtk_widget_show ( item );
6818 if ( wp && wp->image )
6820 if ( !separator_created ) {
6821 item = gtk_menu_item_new ();
6822 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6823 gtk_widget_show ( item );
6824 separator_created = TRUE;
6827 // Set up image paramater
6828 pass_along[5] = wp->image;
6830 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6831 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
6832 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6833 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6834 gtk_widget_show ( item );
6836 #ifdef VIK_CONFIG_GEOTAG
6837 GtkWidget *geotag_submenu = gtk_menu_new ();
6838 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6839 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6840 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6841 gtk_widget_show ( item );
6842 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6844 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6846 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6847 gtk_widget_show ( item );
6849 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6850 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6851 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6852 gtk_widget_show ( item );
6858 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6859 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6860 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6861 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6863 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6864 gtk_widget_show ( item );
6871 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6872 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6874 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6875 gtk_widget_show ( item );
6876 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6877 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6878 gtk_widget_set_sensitive ( item, TRUE );
6880 gtk_widget_set_sensitive ( item, FALSE );
6883 item = gtk_menu_item_new ();
6884 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6885 gtk_widget_show ( item );
6888 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6891 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
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_wp), pass_along );
6894 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6895 gtk_widget_show ( item );
6898 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6900 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6901 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6903 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6904 gtk_widget_show ( item );
6906 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6907 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6909 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6910 gtk_widget_show ( item );
6912 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6913 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6915 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6916 gtk_widget_show ( item );
6918 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6919 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6921 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6922 gtk_widget_show ( item );
6924 GtkWidget *vis_submenu = gtk_menu_new ();
6925 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6926 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6927 gtk_widget_show ( item );
6928 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6930 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
6931 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6932 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
6933 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6934 gtk_widget_show ( item );
6936 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
6937 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6938 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
6939 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6940 gtk_widget_show ( item );
6942 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6943 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
6945 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6946 gtk_widget_show ( item );
6949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6953 if ( l->current_track && !l->current_track->is_route ) {
6954 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6955 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6956 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6957 gtk_widget_show ( item );
6959 item = gtk_menu_item_new ();
6960 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6961 gtk_widget_show ( item );
6964 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6965 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6966 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6967 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6968 gtk_widget_show ( item );
6970 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6973 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6974 gtk_widget_show ( item );
6975 // Make it available only when a new track *not* already in progress
6976 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6978 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6979 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6982 gtk_widget_show ( item );
6984 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6985 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6987 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6988 gtk_widget_show ( item );
6990 GtkWidget *vis_submenu = gtk_menu_new ();
6991 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6992 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6993 gtk_widget_show ( item );
6994 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6996 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
6997 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
6999 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7000 gtk_widget_show ( item );
7002 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7003 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7005 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7006 gtk_widget_show ( item );
7008 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7009 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7010 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7011 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7012 gtk_widget_show ( item );
7014 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7016 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7017 gtk_widget_show ( item );
7020 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7024 if ( l->current_track && l->current_track->is_route ) {
7025 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7026 // Reuse finish track method
7027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7028 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7029 gtk_widget_show ( item );
7031 item = gtk_menu_item_new ();
7032 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7033 gtk_widget_show ( item );
7036 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7037 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7039 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7040 gtk_widget_show ( item );
7042 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7043 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7044 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7045 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7046 gtk_widget_show ( item );
7047 // Make it available only when a new track *not* already in progress
7048 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7050 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7051 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7053 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7054 gtk_widget_show ( item );
7056 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7057 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7059 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7060 gtk_widget_show ( item );
7062 GtkWidget *vis_submenu = gtk_menu_new ();
7063 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7064 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7065 gtk_widget_show ( item );
7066 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7068 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7071 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7072 gtk_widget_show ( item );
7074 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7077 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7078 gtk_widget_show ( item );
7080 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7081 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7083 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7084 gtk_widget_show ( item );
7086 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7087 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7088 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7089 gtk_widget_show ( item );
7093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7094 GtkWidget *submenu_sort = gtk_menu_new ();
7095 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7096 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7097 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7098 gtk_widget_show ( item );
7099 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7101 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7104 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7105 gtk_widget_show ( item );
7107 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7108 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7110 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7111 gtk_widget_show ( item );
7114 GtkWidget *upload_submenu = gtk_menu_new ();
7116 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7118 item = gtk_menu_item_new ();
7119 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7120 gtk_widget_show ( item );
7122 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7123 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7124 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7125 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7126 if ( l->current_track ) {
7127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7128 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7129 gtk_widget_show ( item );
7132 item = gtk_menu_item_new ();
7133 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7134 gtk_widget_show ( item );
7137 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7138 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7140 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7141 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7143 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7144 gtk_widget_show ( item );
7146 GtkWidget *goto_submenu;
7147 goto_submenu = gtk_menu_new ();
7148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7151 gtk_widget_show ( item );
7152 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7154 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7155 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7156 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7157 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7158 gtk_widget_show ( item );
7160 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7161 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7162 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7163 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7164 gtk_widget_show ( item );
7166 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7167 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7168 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7169 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7170 gtk_widget_show ( item );
7172 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7173 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7175 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7176 gtk_widget_show ( item );
7178 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7179 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7181 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7182 gtk_widget_show ( item );
7184 // Routes don't have speeds
7185 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7186 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7189 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7190 gtk_widget_show ( item );
7193 GtkWidget *combine_submenu;
7194 combine_submenu = gtk_menu_new ();
7195 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7196 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7197 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7198 gtk_widget_show ( item );
7199 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7201 // Routes don't have times or segments...
7202 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7203 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7205 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7206 gtk_widget_show ( item );
7208 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7210 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7211 gtk_widget_show ( item );
7214 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7216 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7217 gtk_widget_show ( item );
7219 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7220 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7222 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7223 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7224 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7225 gtk_widget_show ( item );
7227 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7228 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7230 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7231 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7232 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7233 gtk_widget_show ( item );
7235 GtkWidget *split_submenu;
7236 split_submenu = gtk_menu_new ();
7237 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7238 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7239 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7240 gtk_widget_show ( item );
7241 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7243 // Routes don't have times or segments...
7244 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7245 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7246 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7247 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7248 gtk_widget_show ( item );
7250 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7251 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7253 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7254 gtk_widget_show ( item );
7257 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7259 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7260 gtk_widget_show ( item );
7262 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7264 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7265 gtk_widget_show ( item );
7266 // Make it available only when a trackpoint is selected.
7267 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7269 GtkWidget *delete_submenu;
7270 delete_submenu = gtk_menu_new ();
7271 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7272 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7273 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7274 gtk_widget_show ( item );
7275 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7277 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7279 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7280 gtk_widget_show ( item );
7282 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7283 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7284 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7285 gtk_widget_show ( item );
7287 GtkWidget *transform_submenu;
7288 transform_submenu = gtk_menu_new ();
7289 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7290 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7291 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7292 gtk_widget_show ( item );
7293 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7295 GtkWidget *dem_submenu;
7296 dem_submenu = gtk_menu_new ();
7297 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7298 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
7299 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7300 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7302 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7304 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7305 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7306 gtk_widget_show ( item );
7308 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7309 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7310 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7311 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7312 gtk_widget_show ( item );
7314 GtkWidget *smooth_submenu;
7315 smooth_submenu = gtk_menu_new ();
7316 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7317 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7318 gtk_widget_show ( item );
7319 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7321 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7323 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7324 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7325 gtk_widget_show ( item );
7327 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7329 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7330 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7331 gtk_widget_show ( item );
7333 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7334 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7336 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7337 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7338 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7339 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7340 gtk_widget_show ( item );
7342 // Routes don't have timestamps - so this is only available for tracks
7343 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7344 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7345 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7346 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7347 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7348 gtk_widget_show ( item );
7351 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7352 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7354 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7355 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7356 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7357 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7358 gtk_widget_show ( item );
7360 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7361 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7362 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7363 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7364 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7365 gtk_widget_show ( item );
7368 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7370 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7371 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7373 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7374 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
7375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7376 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7377 gtk_widget_show ( item );
7380 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7381 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7383 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7384 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7385 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7386 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7387 gtk_widget_show ( item );
7389 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7390 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7392 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7395 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7396 gtk_widget_show ( item );
7398 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7399 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7400 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
7401 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7402 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7403 gtk_widget_show ( item );
7406 // ATM can't upload a single waypoint but can do waypoints to a GPS
7407 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7408 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7409 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7410 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7411 gtk_widget_show ( item );
7412 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7414 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7415 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7417 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7418 gtk_widget_show ( item );
7422 #ifdef VIK_CONFIG_GOOGLE
7423 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7425 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7426 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7427 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7428 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7429 gtk_widget_show ( item );
7433 // Some things aren't usable with routes
7434 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7435 #ifdef VIK_CONFIG_OPENSTREETMAP
7436 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7437 // Convert internal pointer into actual track for usage outside this file
7438 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7439 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7440 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7441 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7442 gtk_widget_show ( item );
7445 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7446 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7448 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7449 gtk_widget_show ( item );
7451 /* ATM This function is only available via the layers panel, due to needing a vlp */
7453 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7454 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7455 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7457 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7458 gtk_widget_show ( item );
7462 #ifdef VIK_CONFIG_GEOTAG
7463 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7464 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7465 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7466 gtk_widget_show ( item );
7470 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7471 // Only show on viewport popmenu when a trackpoint is selected
7472 if ( ! vlp && l->current_tpl ) {
7474 item = gtk_menu_item_new ();
7475 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7476 gtk_widget_show ( item );
7478 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7479 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7481 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7482 gtk_widget_show ( item );
7486 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7487 GtkWidget *transform_submenu;
7488 transform_submenu = gtk_menu_new ();
7489 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7490 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7491 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7492 gtk_widget_show ( item );
7493 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7495 GtkWidget *dem_submenu;
7496 dem_submenu = gtk_menu_new ();
7497 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7498 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
7499 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7500 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7502 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
7504 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7505 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7506 gtk_widget_show ( item );
7508 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7509 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
7510 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7511 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7512 gtk_widget_show ( item );
7515 gtk_widget_show_all ( GTK_WIDGET(menu) );
7520 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7523 if (!vtl->current_tpl)
7525 if (!vtl->current_tpl->next)
7528 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7529 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7531 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7534 VikTrackpoint *tp_new = vik_trackpoint_new();
7535 struct LatLon ll_current, ll_next;
7536 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7537 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7539 /* main positional interpolation */
7540 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7541 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7543 /* Now other properties that can be interpolated */
7544 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7546 if (tp_current->has_timestamp && tp_next->has_timestamp) {
7547 /* Note here the division is applied to each part, then added
7548 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7549 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7550 tp_new->has_timestamp = TRUE;
7553 if (tp_current->speed != NAN && tp_next->speed != NAN)
7554 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7556 /* TODO - improve interpolation of course, as it may not be correct.
7557 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7558 [similar applies if value is in radians] */
7559 if (tp_current->course != NAN && tp_next->course != NAN)
7560 tp_new->course = (tp_current->course + tp_next->course)/2;
7562 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7564 /* Insert new point into the trackpoints list after the current TP */
7565 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7567 // Otherwise try routes
7568 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7572 gint index = g_list_index ( trk->trackpoints, tp_current );
7574 // NB no recalculation of bounds since it is inserted between points
7575 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7580 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7586 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7590 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7592 if ( vtl->current_tpl )
7594 vtl->current_tpl = NULL;
7595 vtl->current_tp_track = NULL;
7596 vtl->current_tp_id = NULL;
7597 vik_layer_emit_update(VIK_LAYER(vtl));
7601 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7603 g_assert ( vtl->tpwin != NULL );
7604 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7605 trw_layer_cancel_current_tp ( vtl, TRUE );
7607 if ( vtl->current_tpl == NULL )
7610 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7612 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7613 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7615 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7617 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7619 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7625 // Find available adjacent trackpoint
7626 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7628 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7629 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7631 // Delete current trackpoint
7632 vik_trackpoint_free ( vtl->current_tpl->data );
7633 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7635 // Set to current to the available adjacent trackpoint
7636 vtl->current_tpl = new_tpl;
7638 // Reset dialog with the available adjacent trackpoint
7639 if ( vtl->current_tp_track ) {
7640 vik_track_calculate_bounds ( vtl->current_tp_track );
7641 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7644 vik_layer_emit_update(VIK_LAYER(vtl));
7648 // Delete current trackpoint
7649 vik_trackpoint_free ( vtl->current_tpl->data );
7650 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7651 trw_layer_cancel_current_tp ( vtl, FALSE );
7654 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7656 if ( vtl->current_tp_track )
7657 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7658 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7660 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7662 if ( vtl->current_tp_track )
7663 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7664 vik_layer_emit_update(VIK_LAYER(vtl));
7666 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7668 trw_layer_insert_tp_after_current_tp ( vtl );
7669 vik_layer_emit_update(VIK_LAYER(vtl));
7671 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7672 vik_layer_emit_update(VIK_LAYER(vtl));
7676 * trw_layer_dialog_shift:
7677 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7679 * Try to reposition a dialog if it's over the specified coord
7680 * so to not obscure the item of interest
7682 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7684 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
7686 // Attempt force dialog to be shown so we can find out where it is more reliably...
7687 while ( gtk_events_pending() )
7688 gtk_main_iteration ();
7690 // get parent window position & size
7691 gint win_pos_x, win_pos_y;
7692 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
7694 gint win_size_x, win_size_y;
7695 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
7697 // get own dialog size
7698 gint dia_size_x, dia_size_y;
7699 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
7701 // get own dialog position
7702 gint dia_pos_x, dia_pos_y;
7703 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
7705 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
7706 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
7708 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
7710 gint vp_xx, vp_yy; // In viewport pixels
7711 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
7713 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
7717 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
7719 // Transform Viewport pixels into absolute pixels
7720 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
7721 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
7723 // Is dialog over the point (to within an ^^ edge value)
7724 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
7725 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
7729 gint hh = vik_viewport_get_height ( vvp );
7731 // Consider the difference in viewport to the full window
7732 gint offset_y = dest_y;
7733 // Add difference between dialog and window sizes
7734 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
7736 if ( vp_yy > hh/2 ) {
7737 // Point in bottom half, move window to top half
7738 gtk_window_move ( dialog, dia_pos_x, offset_y );
7741 // Point in top half, move dialog down
7742 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
7746 // Shift left<->right
7747 gint ww = vik_viewport_get_width ( vvp );
7749 // Consider the difference in viewport to the full window
7750 gint offset_x = dest_x;
7751 // Add difference between dialog and window sizes
7752 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
7754 if ( vp_xx > ww/2 ) {
7755 // Point on right, move window to left
7756 gtk_window_move ( dialog, offset_x, dia_pos_y );
7759 // Point on left, move right
7760 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7768 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7772 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7773 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7774 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7775 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7777 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7779 if ( vtl->current_tpl ) {
7780 // get tp pixel position
7781 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7783 // Shift up<->down to try not to obscure the trackpoint.
7784 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7788 if ( vtl->current_tpl )
7789 if ( vtl->current_tp_track )
7790 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7791 /* set layer name and TP data */
7794 /***************************************************************************
7796 ***************************************************************************/
7798 /*** Utility data structures and functions ****/
7802 gint closest_x, closest_y;
7803 gboolean draw_images;
7804 gpointer *closest_wp_id;
7805 VikWaypoint *closest_wp;
7811 gint closest_x, closest_y;
7812 gpointer closest_track_id;
7813 VikTrackpoint *closest_tp;
7819 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7825 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7827 // If waypoint has an image then use the image size to select
7828 if ( params->draw_images && wp->image ) {
7829 gint slackx, slacky;
7830 slackx = wp->image_width / 2;
7831 slacky = wp->image_height / 2;
7833 if ( x <= params->x + slackx && x >= params->x - slackx
7834 && y <= params->y + slacky && y >= params->y - slacky ) {
7835 params->closest_wp_id = id;
7836 params->closest_wp = wp;
7837 params->closest_x = x;
7838 params->closest_y = y;
7841 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7842 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7843 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7845 params->closest_wp_id = id;
7846 params->closest_wp = wp;
7847 params->closest_x = x;
7848 params->closest_y = y;
7852 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7854 GList *tpl = t->trackpoints;
7860 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7866 tp = VIK_TRACKPOINT(tpl->data);
7868 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7870 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7871 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7872 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7874 params->closest_track_id = id;
7875 params->closest_tp = tp;
7876 params->closest_tpl = tpl;
7877 params->closest_x = x;
7878 params->closest_y = y;
7884 // ATM: Leave this as 'Track' only.
7885 // Not overly bothered about having a snap to route trackpoint capability
7886 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7888 TPSearchParams params;
7892 params.closest_track_id = NULL;
7893 params.closest_tp = NULL;
7894 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7895 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7896 return params.closest_tp;
7899 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7901 WPSearchParams params;
7905 params.draw_images = vtl->drawimages;
7906 params.closest_wp = NULL;
7907 params.closest_wp_id = NULL;
7908 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7909 return params.closest_wp;
7913 // Some forward declarations
7914 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7915 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7916 static void marker_end_move ( tool_ed_t *t );
7919 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7923 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7925 // Here always allow snapping back to the original location
7926 // this is useful when one decides not to move the thing afterall
7927 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7930 if ( event->state & GDK_CONTROL_MASK )
7932 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7934 new_coord = tp->coord;
7938 if ( event->state & GDK_SHIFT_MASK )
7940 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7942 new_coord = wp->coord;
7946 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7948 marker_moveto ( t, x, y );
7955 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7957 if ( t->holding && event->button == 1 )
7960 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7963 if ( event->state & GDK_CONTROL_MASK )
7965 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7967 new_coord = tp->coord;
7971 if ( event->state & GDK_SHIFT_MASK )
7973 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7975 new_coord = wp->coord;
7978 marker_end_move ( t );
7980 // Determine if working on a waypoint or a trackpoint
7981 if ( t->is_waypoint ) {
7982 vtl->current_wp->coord = new_coord;
7983 trw_layer_calculate_bounds_waypoints ( vtl );
7986 if ( vtl->current_tpl ) {
7987 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7989 if ( vtl->current_tp_track )
7990 vik_track_calculate_bounds ( vtl->current_tp_track );
7993 if ( vtl->current_tp_track )
7994 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7999 vtl->current_wp = NULL;
8000 vtl->current_wp_id = NULL;
8001 trw_layer_cancel_current_tp ( vtl, FALSE );
8003 vik_layer_emit_update ( VIK_LAYER(vtl) );
8010 Returns true if a waypoint or track is found near the requested event position for this particular layer
8011 The item found is automatically selected
8012 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8014 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8016 if ( event->button != 1 )
8019 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8022 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8026 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8028 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8030 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8031 WPSearchParams wp_params;
8032 wp_params.vvp = vvp;
8033 wp_params.x = event->x;
8034 wp_params.y = event->y;
8035 wp_params.draw_images = vtl->drawimages;
8036 wp_params.closest_wp_id = NULL;
8037 wp_params.closest_wp = NULL;
8039 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8041 if ( wp_params.closest_wp ) {
8044 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8046 // Too easy to move it so must be holding shift to start immediately moving it
8047 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8048 if ( event->state & GDK_SHIFT_MASK ||
8049 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8050 // Put into 'move buffer'
8051 // NB vvp & vw already set in tet
8052 tet->vtl = (gpointer)vtl;
8053 tet->is_waypoint = TRUE;
8055 marker_begin_move (tet, event->x, event->y);
8058 vtl->current_wp = wp_params.closest_wp;
8059 vtl->current_wp_id = wp_params.closest_wp_id;
8061 vik_layer_emit_update ( VIK_LAYER(vtl) );
8067 // Used for both track and route lists
8068 TPSearchParams tp_params;
8069 tp_params.vvp = vvp;
8070 tp_params.x = event->x;
8071 tp_params.y = event->y;
8072 tp_params.closest_track_id = NULL;
8073 tp_params.closest_tp = NULL;
8074 tp_params.closest_tpl = NULL;
8075 tp_params.bbox = bbox;
8077 if (vtl->tracks_visible) {
8078 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8080 if ( tp_params.closest_tp ) {
8082 // Always select + highlight the track
8083 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8085 tet->is_waypoint = FALSE;
8087 // Select the Trackpoint
8088 // Can move it immediately when control held or it's the previously selected tp
8089 if ( event->state & GDK_CONTROL_MASK ||
8090 vtl->current_tpl == tp_params.closest_tpl ) {
8091 // Put into 'move buffer'
8092 // NB vvp & vw already set in tet
8093 tet->vtl = (gpointer)vtl;
8094 marker_begin_move (tet, event->x, event->y);
8097 vtl->current_tpl = tp_params.closest_tpl;
8098 vtl->current_tp_id = tp_params.closest_track_id;
8099 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8101 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8104 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8106 vik_layer_emit_update ( VIK_LAYER(vtl) );
8111 // Try again for routes
8112 if (vtl->routes_visible) {
8113 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8115 if ( tp_params.closest_tp ) {
8117 // Always select + highlight the track
8118 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8120 tet->is_waypoint = FALSE;
8122 // Select the Trackpoint
8123 // Can move it immediately when control held or it's the previously selected tp
8124 if ( event->state & GDK_CONTROL_MASK ||
8125 vtl->current_tpl == tp_params.closest_tpl ) {
8126 // Put into 'move buffer'
8127 // NB vvp & vw already set in tet
8128 tet->vtl = (gpointer)vtl;
8129 marker_begin_move (tet, event->x, event->y);
8132 vtl->current_tpl = tp_params.closest_tpl;
8133 vtl->current_tp_id = tp_params.closest_track_id;
8134 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8136 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8139 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8141 vik_layer_emit_update ( VIK_LAYER(vtl) );
8146 /* these aren't the droids you're looking for */
8147 vtl->current_wp = NULL;
8148 vtl->current_wp_id = NULL;
8149 trw_layer_cancel_current_tp ( vtl, FALSE );
8152 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8157 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8159 if ( event->button != 3 )
8162 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8165 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8168 /* Post menu for the currently selected item */
8170 /* See if a track is selected */
8171 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8172 if ( track && track->visible ) {
8174 if ( track->name ) {
8176 if ( vtl->track_right_click_menu )
8177 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8179 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8186 if ( track->is_route )
8187 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8189 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8191 if ( trkf && udataU.uuid ) {
8194 if ( track->is_route )
8195 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8197 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8199 trw_layer_sublayer_add_menu_items ( vtl,
8200 vtl->track_right_click_menu,
8202 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8208 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8214 /* See if a waypoint is selected */
8215 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8216 if ( waypoint && waypoint->visible ) {
8217 if ( waypoint->name ) {
8219 if ( vtl->wp_right_click_menu )
8220 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8222 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8225 udata.wp = waypoint;
8228 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8230 if ( wpf && udata.uuid ) {
8231 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8233 trw_layer_sublayer_add_menu_items ( vtl,
8234 vtl->wp_right_click_menu,
8236 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8241 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8250 /* background drawing hook, to be passed the viewport */
8251 static gboolean tool_sync_done = TRUE;
8253 static gboolean tool_sync(gpointer data)
8255 VikViewport *vvp = data;
8256 gdk_threads_enter();
8257 vik_viewport_sync(vvp);
8258 tool_sync_done = TRUE;
8259 gdk_threads_leave();
8263 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8266 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8267 gdk_gc_set_function ( t->gc, GDK_INVERT );
8268 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8269 vik_viewport_sync(t->vvp);
8274 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8276 VikViewport *vvp = t->vvp;
8277 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8278 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8282 if (tool_sync_done) {
8283 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8284 tool_sync_done = FALSE;
8288 static void marker_end_move ( tool_ed_t *t )
8290 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8291 g_object_unref ( t->gc );
8295 /*** Edit waypoint ****/
8297 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8299 tool_ed_t *t = g_new(tool_ed_t, 1);
8305 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8310 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8312 WPSearchParams params;
8313 tool_ed_t *t = data;
8314 VikViewport *vvp = t->vvp;
8316 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8323 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8326 if ( vtl->current_wp && vtl->current_wp->visible )
8328 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8330 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8332 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8333 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8335 if ( event->button == 3 )
8336 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8338 marker_begin_move(t, event->x, event->y);
8345 params.x = event->x;
8346 params.y = event->y;
8347 params.draw_images = vtl->drawimages;
8348 params.closest_wp_id = NULL;
8349 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8350 params.closest_wp = NULL;
8351 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8352 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8354 // how do we get here?
8355 marker_begin_move(t, event->x, event->y);
8356 g_critical("shouldn't be here");
8359 else if ( params.closest_wp )
8361 if ( event->button == 3 )
8362 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8364 vtl->waypoint_rightclick = FALSE;
8366 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8368 vtl->current_wp = params.closest_wp;
8369 vtl->current_wp_id = params.closest_wp_id;
8371 /* could make it so don't update if old WP is off screen and new is null but oh well */
8372 vik_layer_emit_update ( VIK_LAYER(vtl) );
8376 vtl->current_wp = NULL;
8377 vtl->current_wp_id = NULL;
8378 vtl->waypoint_rightclick = FALSE;
8379 vik_layer_emit_update ( VIK_LAYER(vtl) );
8383 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8385 tool_ed_t *t = data;
8386 VikViewport *vvp = t->vvp;
8388 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8393 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8396 if ( event->state & GDK_CONTROL_MASK )
8398 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8400 new_coord = tp->coord;
8404 if ( event->state & GDK_SHIFT_MASK )
8406 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8407 if ( wp && wp != vtl->current_wp )
8408 new_coord = wp->coord;
8413 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8415 marker_moveto ( t, x, y );
8422 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8424 tool_ed_t *t = data;
8425 VikViewport *vvp = t->vvp;
8427 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8430 if ( t->holding && event->button == 1 )
8433 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8436 if ( event->state & GDK_CONTROL_MASK )
8438 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8440 new_coord = tp->coord;
8444 if ( event->state & GDK_SHIFT_MASK )
8446 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8447 if ( wp && wp != vtl->current_wp )
8448 new_coord = wp->coord;
8451 marker_end_move ( t );
8453 vtl->current_wp->coord = new_coord;
8455 trw_layer_calculate_bounds_waypoints ( vtl );
8456 vik_layer_emit_update ( VIK_LAYER(vtl) );
8459 /* PUT IN RIGHT PLACE!!! */
8460 if ( event->button == 3 && vtl->waypoint_rightclick )
8462 if ( vtl->wp_right_click_menu )
8463 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8464 if ( vtl->current_wp ) {
8465 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8466 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 );
8467 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8469 vtl->waypoint_rightclick = FALSE;
8474 /*** New track ****/
8476 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8483 GdkDrawable *drawable;
8489 * Draw specified pixmap
8491 static gboolean draw_sync ( gpointer data )
8493 draw_sync_t *ds = (draw_sync_t*) data;
8494 // Sometimes don't want to draw
8495 // normally because another update has taken precedent such as panning the display
8496 // which means this pixmap is no longer valid
8497 if ( ds->vtl->draw_sync_do ) {
8498 gdk_threads_enter();
8499 gdk_draw_drawable (ds->drawable,
8502 0, 0, 0, 0, -1, -1);
8503 ds->vtl->draw_sync_done = TRUE;
8504 gdk_threads_leave();
8510 static gchar* distance_string (gdouble distance)
8514 /* draw label with distance */
8515 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8516 switch (dist_units) {
8517 case VIK_UNITS_DISTANCE_MILES:
8518 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8519 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8520 } else if (distance < 1609.4) {
8521 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8523 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8527 // VIK_UNITS_DISTANCE_KILOMETRES
8528 if (distance >= 1000 && distance < 100000) {
8529 g_sprintf(str, "%3.2f km", distance/1000.0);
8530 } else if (distance < 1000) {
8531 g_sprintf(str, "%d m", (int)distance);
8533 g_sprintf(str, "%d km", (int)distance/1000);
8537 return g_strdup (str);
8541 * Actually set the message in statusbar
8543 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8545 // Only show elevation data when track has some elevation properties
8546 gchar str_gain_loss[64];
8547 str_gain_loss[0] = '\0';
8548 gchar str_last_step[64];
8549 str_last_step[0] = '\0';
8550 gchar *str_total = distance_string (distance);
8552 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8553 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8554 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8556 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8559 if ( last_step > 0 ) {
8560 gchar *tmp = distance_string (last_step);
8561 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8565 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8567 // Write with full gain/loss information
8568 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8569 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8571 g_free ( str_total );
8575 * Figure out what information should be set in the statusbar and then write it
8577 static void update_statusbar ( VikTrwLayer *vtl )
8579 // Get elevation data
8580 gdouble elev_gain, elev_loss;
8581 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8583 /* Find out actual distance of current track */
8584 gdouble distance = vik_track_get_length (vtl->current_track);
8586 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8590 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8592 /* if we haven't sync'ed yet, we don't have time to do more. */
8593 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8594 GList *iter = g_list_last ( vtl->current_track->trackpoints );
8595 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8597 static GdkPixmap *pixmap = NULL;
8599 // Need to check in case window has been resized
8600 w1 = vik_viewport_get_width(vvp);
8601 h1 = vik_viewport_get_height(vvp);
8603 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8605 gdk_drawable_get_size (pixmap, &w2, &h2);
8606 if (w1 != w2 || h1 != h2) {
8607 g_object_unref ( G_OBJECT ( pixmap ) );
8608 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8611 // Reset to background
8612 gdk_draw_drawable (pixmap,
8613 vtl->current_track_newpoint_gc,
8614 vik_viewport_get_pixmap(vvp),
8615 0, 0, 0, 0, -1, -1);
8617 draw_sync_t *passalong;
8620 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8622 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8623 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8624 // thus when we come to reset to the background it would include what we have already drawn!!
8625 gdk_draw_line ( pixmap,
8626 vtl->current_track_newpoint_gc,
8627 x1, y1, event->x, event->y );
8628 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8630 /* Find out actual distance of current track */
8631 gdouble distance = vik_track_get_length (vtl->current_track);
8633 // Now add distance to where the pointer is //
8636 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8637 vik_coord_to_latlon ( &coord, &ll );
8638 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8639 distance = distance + last_step;
8641 // Get elevation data
8642 gdouble elev_gain, elev_loss;
8643 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8645 // Adjust elevation data (if available) for the current pointer position
8647 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8648 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8649 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8650 // Adjust elevation of last track point
8651 if ( elev_new > last_tpt->altitude )
8653 elev_gain += elev_new - last_tpt->altitude;
8656 elev_loss += last_tpt->altitude - elev_new;
8661 // Display of the distance 'tooltip' during track creation is controlled by a preference
8663 if ( a_vik_get_create_track_tooltip() ) {
8665 gchar *str = distance_string (distance);
8667 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8668 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8669 pango_layout_set_text (pl, str, -1);
8671 pango_layout_get_pixel_size ( pl, &wd, &hd );
8674 // offset from cursor a bit depending on font size
8678 // Create a background block to make the text easier to read over the background map
8679 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8680 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8681 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8683 g_object_unref ( G_OBJECT ( pl ) );
8684 g_object_unref ( G_OBJECT ( background_block_gc ) );
8688 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
8689 passalong->vtl = vtl;
8690 passalong->pixmap = pixmap;
8691 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
8692 passalong->gc = vtl->current_track_newpoint_gc;
8696 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
8698 // Update statusbar with full gain/loss information
8699 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
8701 // draw pixmap when we have time to
8702 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
8703 vtl->draw_sync_done = FALSE;
8704 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
8706 return VIK_LAYER_TOOL_ACK;
8709 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
8711 if ( vtl->current_track && event->keyval == GDK_Escape ) {
8712 vtl->current_track = NULL;
8713 vik_layer_emit_update ( VIK_LAYER(vtl) );
8715 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
8717 if ( vtl->current_track->trackpoints )
8719 GList *last = g_list_last(vtl->current_track->trackpoints);
8720 g_free ( last->data );
8721 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8724 update_statusbar ( vtl );
8726 vik_layer_emit_update ( VIK_LAYER(vtl) );
8733 * Common function to handle trackpoint button requests on either a route or a track
8734 * . enables adding a point via normal click
8735 * . enables removal of last point via right click
8736 * . finishing of the track or route via double clicking
8738 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8742 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8745 if ( event->button == 2 ) {
8746 // As the display is panning, the new track pixmap is now invalid so don't draw it
8747 // otherwise this drawing done results in flickering back to an old image
8748 vtl->draw_sync_do = FALSE;
8752 if ( event->button == 3 )
8754 if ( !vtl->current_track )
8757 if ( vtl->current_track->trackpoints )
8759 GList *last = g_list_last(vtl->current_track->trackpoints);
8760 g_free ( last->data );
8761 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8763 vik_track_calculate_bounds ( vtl->current_track );
8764 update_statusbar ( vtl );
8766 vik_layer_emit_update ( VIK_LAYER(vtl) );
8770 if ( event->type == GDK_2BUTTON_PRESS )
8772 /* subtract last (duplicate from double click) tp then end */
8773 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8775 GList *last = g_list_last(vtl->current_track->trackpoints);
8776 g_free ( last->data );
8777 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8778 /* undo last, then end */
8779 vtl->current_track = NULL;
8781 vik_layer_emit_update ( VIK_LAYER(vtl) );
8785 tp = vik_trackpoint_new();
8786 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8788 /* snap to other TP */
8789 if ( event->state & GDK_CONTROL_MASK )
8791 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8793 tp->coord = other_tp->coord;
8796 tp->newsegment = FALSE;
8797 tp->has_timestamp = FALSE;
8800 if ( vtl->current_track ) {
8801 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
8802 /* Auto attempt to get elevation from DEM data (if it's available) */
8803 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8806 vtl->ct_x1 = vtl->ct_x2;
8807 vtl->ct_y1 = vtl->ct_y2;
8808 vtl->ct_x2 = event->x;
8809 vtl->ct_y2 = event->y;
8811 vik_layer_emit_update ( VIK_LAYER(vtl) );
8815 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8817 // ----------------------------------------------------- if current is a route - switch to new track
8818 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8820 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8821 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8823 new_track_create_common ( vtl, name );
8829 return tool_new_track_or_route_click ( vtl, event, vvp );
8832 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8834 if ( event->button == 2 ) {
8835 // Pan moving ended - enable potential point drawing again
8836 vtl->draw_sync_do = TRUE;
8837 vtl->draw_sync_done = TRUE;
8841 /*** New route ****/
8843 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8848 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8850 // -------------------------- if current is a track - switch to new route
8851 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8853 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8854 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) ) {
8855 new_route_create_common ( vtl, name );
8861 return tool_new_track_or_route_click ( vtl, event, vvp );
8864 /*** New waypoint ****/
8866 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8871 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8874 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8876 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8877 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8878 trw_layer_calculate_bounds_waypoints ( vtl );
8879 vik_layer_emit_update ( VIK_LAYER(vtl) );
8885 /*** Edit trackpoint ****/
8887 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8889 tool_ed_t *t = g_new(tool_ed_t, 1);
8895 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
8900 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8902 tool_ed_t *t = data;
8903 VikViewport *vvp = t->vvp;
8904 TPSearchParams params;
8905 /* OUTDATED DOCUMENTATION:
8906 find 5 pixel range on each side. then put these UTM, and a pointer
8907 to the winning track name (and maybe the winning track itself), and a
8908 pointer to the winning trackpoint, inside an array or struct. pass
8909 this along, do a foreach on the tracks which will do a foreach on the
8912 params.x = event->x;
8913 params.y = event->y;
8914 params.closest_track_id = NULL;
8915 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8916 params.closest_tp = NULL;
8917 params.closest_tpl = NULL;
8918 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8920 if ( event->button != 1 )
8923 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8926 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8929 if ( vtl->current_tpl )
8931 /* 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.) */
8932 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8933 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8938 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8940 if ( current_tr->visible &&
8941 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8942 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8943 marker_begin_move ( t, event->x, event->y );
8949 if ( vtl->tracks_visible )
8950 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8952 if ( params.closest_tp )
8954 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8955 vtl->current_tpl = params.closest_tpl;
8956 vtl->current_tp_id = params.closest_track_id;
8957 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8958 trw_layer_tpwin_init ( vtl );
8959 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8960 vik_layer_emit_update ( VIK_LAYER(vtl) );
8964 if ( vtl->routes_visible )
8965 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8967 if ( params.closest_tp )
8969 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8970 vtl->current_tpl = params.closest_tpl;
8971 vtl->current_tp_id = params.closest_track_id;
8972 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8973 trw_layer_tpwin_init ( vtl );
8974 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8975 vik_layer_emit_update ( VIK_LAYER(vtl) );
8979 /* these aren't the droids you're looking for */
8983 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8985 tool_ed_t *t = data;
8986 VikViewport *vvp = t->vvp;
8988 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8994 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8997 if ( event->state & GDK_CONTROL_MASK )
8999 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9000 if ( tp && tp != vtl->current_tpl->data )
9001 new_coord = tp->coord;
9003 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9006 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9007 marker_moveto ( t, x, y );
9015 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9017 tool_ed_t *t = data;
9018 VikViewport *vvp = t->vvp;
9020 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9022 if ( event->button != 1)
9027 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9030 if ( event->state & GDK_CONTROL_MASK )
9032 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9033 if ( tp && tp != vtl->current_tpl->data )
9034 new_coord = tp->coord;
9037 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9038 if ( vtl->current_tp_track )
9039 vik_track_calculate_bounds ( vtl->current_tp_track );
9041 marker_end_move ( t );
9043 /* diff dist is diff from orig */
9045 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9047 vik_layer_emit_update ( VIK_LAYER(vtl) );
9054 /*** Route Finder ***/
9055 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9060 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9063 if ( !vtl ) return FALSE;
9064 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9065 if ( event->button == 3 && vtl->route_finder_current_track ) {
9067 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9069 vtl->route_finder_coord = *new_end;
9071 vik_layer_emit_update ( VIK_LAYER(vtl) );
9072 /* remove last ' to:...' */
9073 if ( vtl->route_finder_current_track->comment ) {
9074 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9075 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9076 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9077 last_to - vtl->route_finder_current_track->comment - 1);
9078 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9083 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9084 struct LatLon start, end;
9086 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9087 vik_coord_to_latlon ( &(tmp), &end );
9088 vtl->route_finder_coord = tmp; /* for continuations */
9090 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9091 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9092 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9094 vtl->route_finder_check_added_track = TRUE;
9095 vtl->route_finder_started = FALSE;
9098 vik_routing_default_find ( vtl, start, end);
9100 /* see if anything was done -- a track was added or appended to */
9101 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9102 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 ) );
9103 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9104 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9105 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9106 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9109 if ( vtl->route_finder_added_track )
9110 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9112 vtl->route_finder_added_track = NULL;
9113 vtl->route_finder_check_added_track = FALSE;
9114 vtl->route_finder_append = FALSE;
9116 vik_layer_emit_update ( VIK_LAYER(vtl) );
9118 vtl->route_finder_started = TRUE;
9119 vtl->route_finder_coord = tmp;
9120 vtl->route_finder_current_track = NULL;
9125 /*** Show picture ****/
9127 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9132 /* Params are: vvp, event, last match found or NULL */
9133 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9135 if ( wp->image && wp->visible )
9137 gint x, y, slackx, slacky;
9138 GdkEventButton *event = (GdkEventButton *) params[1];
9140 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9141 slackx = wp->image_width / 2;
9142 slacky = wp->image_height / 2;
9143 if ( x <= event->x + slackx && x >= event->x - slackx
9144 && y <= event->y + slacky && y >= event->y - slacky )
9146 params[2] = wp->image; /* we've found a match. however continue searching
9147 * since we want to find the last match -- that
9148 * is, the match that was drawn last. */
9153 static void trw_layer_show_picture ( gpointer pass_along[6] )
9155 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9157 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9160 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9161 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9162 g_free ( quoted_file );
9163 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9165 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() );
9166 g_error_free ( err );
9169 #endif /* WINDOWS */
9172 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9174 gpointer params[3] = { vvp, event, NULL };
9175 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9177 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9180 static gpointer pass_along[6];
9181 pass_along[0] = vtl;
9182 pass_along[5] = params[2];
9183 trw_layer_show_picture ( pass_along );
9184 return TRUE; /* found a match */
9187 return FALSE; /* go through other layers, searching for a match */
9190 /***************************************************************************
9192 ***************************************************************************/
9195 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9197 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9198 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9201 /* Structure for thumbnail creating data used in the background thread */
9203 VikTrwLayer *vtl; // Layer needed for redrawing
9204 GSList *pics; // Image list
9205 } thumbnail_create_thread_data;
9207 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9209 guint total = g_slist_length(tctd->pics), done = 0;
9210 while ( tctd->pics )
9212 a_thumbnails_create ( (gchar *) tctd->pics->data );
9213 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9215 return -1; /* Abort thread */
9217 tctd->pics = tctd->pics->next;
9220 // Redraw to show the thumbnails as they are now created
9221 if ( IS_VIK_LAYER(tctd->vtl) )
9222 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9227 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9229 while ( tctd->pics )
9231 g_free ( tctd->pics->data );
9232 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9237 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9239 if ( ! vtl->has_verified_thumbnails )
9241 GSList *pics = NULL;
9242 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9245 gint len = g_slist_length ( pics );
9246 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9247 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9250 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9252 (vik_thr_func) create_thumbnails_thread,
9254 (vik_thr_free_func) thumbnail_create_thread_free,
9262 static const gchar* my_track_colors ( gint ii )
9264 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9276 // Fast and reliable way of returning a colour
9277 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9280 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9282 GHashTableIter iter;
9283 gpointer key, value;
9287 g_hash_table_iter_init ( &iter, vtl->tracks );
9289 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9291 // Tracks get a random spread of colours if not already assigned
9292 if ( ! VIK_TRACK(value)->has_color ) {
9293 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9294 VIK_TRACK(value)->color = vtl->track_color;
9296 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9298 VIK_TRACK(value)->has_color = TRUE;
9301 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9304 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9310 g_hash_table_iter_init ( &iter, vtl->routes );
9312 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9314 // Routes get an intermix of reds
9315 if ( ! VIK_TRACK(value)->has_color ) {
9317 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9319 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9320 VIK_TRACK(value)->has_color = TRUE;
9323 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9330 * (Re)Calculate the bounds of the waypoints in this layer,
9331 * This should be called whenever waypoints are changed
9333 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9335 struct LatLon topleft = { 0.0, 0.0 };
9336 struct LatLon bottomright = { 0.0, 0.0 };
9339 GHashTableIter iter;
9340 gpointer key, value;
9342 g_hash_table_iter_init ( &iter, vtl->waypoints );
9344 // Set bounds to first point
9345 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9346 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9347 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9350 // Ensure there is another point...
9351 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9353 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9355 // See if this point increases the bounds.
9356 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9358 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9359 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9360 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9361 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9365 vtl->waypoints_bbox.north = topleft.lat;
9366 vtl->waypoints_bbox.east = bottomright.lon;
9367 vtl->waypoints_bbox.south = bottomright.lat;
9368 vtl->waypoints_bbox.west = topleft.lon;
9371 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9373 vik_track_calculate_bounds ( trk );
9376 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9378 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9379 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9382 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9384 if ( ! VIK_LAYER(vtl)->vt )
9387 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9388 if ( g_hash_table_size (vtl->tracks) > 1 )
9389 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9391 if ( g_hash_table_size (vtl->routes) > 1 )
9392 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9394 if ( g_hash_table_size (vtl->waypoints) > 1 )
9395 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9398 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9400 trw_layer_verify_thumbnails ( vtl, vvp );
9401 trw_layer_track_alloc_colors ( vtl );
9403 trw_layer_calculate_bounds_waypoints ( vtl );
9404 trw_layer_calculate_bounds_tracks ( vtl );
9406 // Apply treeview sort after loading all the tracks for this layer
9407 // (rather than sorted insert on each individual track additional)
9408 // and after subsequent changes to the properties as the specified order may have changed.
9409 // since the sorting of a treeview section is now very quick
9410 // NB sorting is also performed after every name change as well to maintain the list order
9411 trw_layer_sort_all ( vtl );
9414 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9416 return vtl->coord_mode;
9420 * Uniquify the whole layer
9421 * Also requires the layers panel as the names shown there need updating too
9422 * Returns whether the operation was successful or not
9424 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9427 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9428 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9429 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9435 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9437 vik_coord_convert ( &(wp->coord), *dest_mode );
9440 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9442 vik_track_convert ( tr, *dest_mode );
9445 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9447 if ( vtl->coord_mode != dest_mode )
9449 vtl->coord_mode = dest_mode;
9450 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9451 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9452 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9456 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9458 vtl->menu_selection = selection;
9461 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9463 return (vtl->menu_selection);
9466 /* ----------- Downloading maps along tracks --------------- */
9468 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9470 /* TODO: calculating based on current size of viewport */
9471 const gdouble w_at_zoom_0_125 = 0.0013;
9472 const gdouble h_at_zoom_0_125 = 0.0011;
9473 gdouble zoom_factor = zoom_level/0.125;
9475 wh->lat = h_at_zoom_0_125 * zoom_factor;
9476 wh->lon = w_at_zoom_0_125 * zoom_factor;
9478 return 0; /* all OK */
9481 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9483 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9484 (dist->lat >= ABS(to->north_south - from->north_south)))
9487 VikCoord *coord = g_malloc(sizeof(VikCoord));
9488 coord->mode = VIK_COORD_LATLON;
9490 if (ABS(gradient) < 1) {
9491 if (from->east_west > to->east_west)
9492 coord->east_west = from->east_west - dist->lon;
9494 coord->east_west = from->east_west + dist->lon;
9495 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9497 if (from->north_south > to->north_south)
9498 coord->north_south = from->north_south - dist->lat;
9500 coord->north_south = from->north_south + dist->lat;
9501 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9507 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9509 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9510 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9512 VikCoord *next = from;
9514 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9516 list = g_list_prepend(list, next);
9522 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9524 typedef struct _Rect {
9529 #define GLRECT(iter) ((Rect *)((iter)->data))
9532 GList *rects_to_download = NULL;
9535 if (get_download_area_width(vvp, zoom_level, &wh))
9538 GList *iter = tr->trackpoints;
9542 gboolean new_map = TRUE;
9543 VikCoord *cur_coord, tl, br;
9546 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9548 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9549 rect = g_malloc(sizeof(Rect));
9552 rect->center = *cur_coord;
9553 rects_to_download = g_list_prepend(rects_to_download, rect);
9558 gboolean found = FALSE;
9559 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9560 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9571 GList *fillins = NULL;
9572 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9573 /* seems that ATM the function get_next_coord works only for LATLON */
9574 if ( cur_coord->mode == VIK_COORD_LATLON ) {
9575 /* fill-ins for far apart points */
9576 GList *cur_rect, *next_rect;
9577 for (cur_rect = rects_to_download;
9578 (next_rect = cur_rect->next) != NULL;
9579 cur_rect = cur_rect->next) {
9580 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9581 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9582 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9586 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9589 GList *iter = fillins;
9591 cur_coord = (VikCoord *)(iter->data);
9592 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9593 rect = g_malloc(sizeof(Rect));
9596 rect->center = *cur_coord;
9597 rects_to_download = g_list_prepend(rects_to_download, rect);
9602 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9603 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9607 for (iter = fillins; iter; iter = iter->next)
9609 g_list_free(fillins);
9611 if (rects_to_download) {
9612 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9613 g_free(rect_iter->data);
9614 g_list_free(rects_to_download);
9618 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9621 gint selected_map, default_map;
9622 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9623 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9624 gint selected_zoom, default_zoom;
9628 VikTrwLayer *vtl = pass_along[0];
9629 VikLayersPanel *vlp = pass_along[1];
9631 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9632 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9634 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9638 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9640 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9641 int num_maps = g_list_length(vmls);
9644 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
9648 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9649 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9651 gchar **np = map_names;
9652 VikMapsLayer **lp = map_layers;
9653 for (i = 0; i < num_maps; i++) {
9654 gboolean dup = FALSE;
9655 vml = (VikMapsLayer *)(vmls->data);
9656 for (j = 0; j < i; j++) { /* no duplicate allowed */
9657 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
9664 *np++ = vik_maps_layer_get_map_label(vml);
9670 num_maps = lp - map_layers;
9672 for (default_map = 0; default_map < num_maps; default_map++) {
9673 /* TODO: check for parent layer's visibility */
9674 if (VIK_LAYER(map_layers[default_map])->visible)
9677 default_map = (default_map == num_maps) ? 0 : default_map;
9679 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9680 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
9681 if (cur_zoom == zoom_vals[default_zoom])
9684 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
9686 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
9689 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9692 for (i = 0; i < num_maps; i++)
9693 g_free(map_names[i]);
9701 /**** lowest waypoint number calculation ***/
9702 static gint highest_wp_number_name_to_number(const gchar *name) {
9703 if ( strlen(name) == 3 ) {
9705 if ( n < 100 && name[0] != '0' )
9707 if ( n < 10 && name[0] != '0' )
9715 static void highest_wp_number_reset(VikTrwLayer *vtl)
9717 vtl->highest_wp_number = -1;
9720 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
9722 /* if is bigger that top, add it */
9723 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
9724 if ( new_wp_num > vtl->highest_wp_number )
9725 vtl->highest_wp_number = new_wp_num;
9728 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
9730 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
9731 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
9732 if ( vtl->highest_wp_number == old_wp_num ) {
9734 vtl->highest_wp_number--;
9736 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9737 /* search down until we find something that *does* exist */
9739 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
9740 vtl->highest_wp_number--;
9741 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9746 /* get lowest unused number */
9747 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
9750 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
9752 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
9753 return g_strdup(buf);