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 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2948 if ( elev != VIK_DEM_INVALID_ELEVATION )
2949 wp->altitude = (gdouble)elev;
2951 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
2953 if ( returned_name )
2956 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2957 g_free (default_name);
2958 g_free (returned_name);
2961 g_free (default_name);
2962 vik_waypoint_free(wp);
2966 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2968 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2969 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2970 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2971 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2972 VikViewport *vvp = vik_window_viewport(vw);
2974 // Note the order is max part first then min part - thus reverse order of use in min_max function:
2975 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2976 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2977 trw_layer_calculate_bounds_waypoints ( vtl );
2978 vik_layers_panel_emit_update ( vlp );
2981 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2983 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2984 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2985 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2987 trw_layer_find_maxmin (vtl, maxmin);
2988 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2989 trw_layer_calculate_bounds_waypoints ( vtl );
2990 vik_layers_panel_emit_update ( vlp );
2993 #ifdef VIK_CONFIG_GEOTAG
2994 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2996 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2998 // Update directly - not changing the mtime
2999 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3002 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3004 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3007 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3011 * Use code in separate file for this feature as reasonably complex
3013 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3015 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3016 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3017 // Unset so can be reverified later if necessary
3018 vtl->has_verified_thumbnails = FALSE;
3020 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3026 static void trw_layer_geotagging ( gpointer lav[2] )
3028 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3029 // Unset so can be reverified later if necessary
3030 vtl->has_verified_thumbnails = FALSE;
3032 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3039 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3042 * Acquire into this TRW Layer straight from GPS Device
3044 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3046 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3047 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3048 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3049 VikViewport *vvp = vik_window_viewport(vw);
3051 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3052 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3056 * Acquire into this TRW Layer from Directions
3058 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3060 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3061 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3062 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3063 VikViewport *vvp = vik_window_viewport(vw);
3065 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3068 #ifdef VIK_CONFIG_OPENSTREETMAP
3070 * Acquire into this TRW Layer from OSM
3072 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3074 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3075 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3076 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3077 VikViewport *vvp = vik_window_viewport(vw);
3079 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3083 * Acquire into this TRW Layer from OSM for 'My' Traces
3085 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3087 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3088 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3089 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3090 VikViewport *vvp = vik_window_viewport(vw);
3092 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3096 #ifdef VIK_CONFIG_GEOCACHES
3098 * Acquire into this TRW Layer from Geocaching.com
3100 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3102 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3103 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3104 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3105 VikViewport *vvp = vik_window_viewport(vw);
3107 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3111 #ifdef VIK_CONFIG_GEOTAG
3113 * Acquire into this TRW Layer from images
3115 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3117 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3118 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3119 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3120 VikViewport *vvp = vik_window_viewport(vw);
3122 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3123 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3125 // Reverify thumbnails as they may have changed
3126 vtl->has_verified_thumbnails = FALSE;
3127 trw_layer_verify_thumbnails ( vtl, NULL );
3131 static void trw_layer_gps_upload ( gpointer lav[2] )
3133 gpointer pass_along[6];
3134 pass_along[0] = lav[0];
3135 pass_along[1] = lav[1];
3136 pass_along[2] = NULL; // No track - operate on the layer
3137 pass_along[3] = NULL;
3138 pass_along[4] = NULL;
3139 pass_along[5] = NULL;
3141 trw_layer_gps_upload_any ( pass_along );
3145 * If pass_along[3] is defined that this will upload just that track
3147 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3149 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3150 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3152 // May not actually get a track here as pass_along[2&3] can be null
3153 VikTrack *track = NULL;
3154 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3155 gboolean xfer_all = FALSE;
3157 if ( pass_along[2] ) {
3159 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3160 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3163 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3164 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3167 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3170 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3174 else if ( !pass_along[4] )
3175 xfer_all = TRUE; // i.e. whole layer
3177 if (track && !track->visible) {
3178 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3182 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3183 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3184 GTK_DIALOG_DESTROY_WITH_PARENT,
3186 GTK_RESPONSE_ACCEPT,
3188 GTK_RESPONSE_REJECT,
3191 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3192 GtkWidget *response_w = NULL;
3193 #if GTK_CHECK_VERSION (2, 20, 0)
3194 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3198 gtk_widget_grab_focus ( response_w );
3200 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3202 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3203 datasource_gps_clean_up ( dgs );
3204 gtk_widget_destroy ( dialog );
3208 // Get info from reused datasource dialog widgets
3209 gchar* protocol = datasource_gps_get_protocol ( dgs );
3210 gchar* port = datasource_gps_get_descriptor ( dgs );
3211 // NB don't free the above strings as they're references to values held elsewhere
3212 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3213 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3214 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3215 gboolean turn_off = datasource_gps_get_off ( dgs );
3217 gtk_widget_destroy ( dialog );
3219 // When called from the viewport - work the corresponding layerspanel:
3221 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3224 // Apply settings to transfer to the GPS device
3231 vik_layers_panel_get_viewport (vlp),
3240 * Acquire into this TRW Layer from any GPS Babel supported file
3242 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3244 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3245 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3246 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3247 VikViewport *vvp = vik_window_viewport(vw);
3249 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3252 static void trw_layer_new_wp ( gpointer lav[2] )
3254 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3255 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3256 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3257 instead return true if you want to update. */
3258 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 ) {
3259 trw_layer_calculate_bounds_waypoints ( vtl );
3260 vik_layers_panel_emit_update ( vlp );
3264 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3266 vtl->current_track = vik_track_new();
3267 vtl->current_track->visible = TRUE;
3268 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3269 // Create track with the preferred colour from the layer properties
3270 vtl->current_track->color = vtl->track_color;
3272 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3273 vtl->current_track->has_color = TRUE;
3274 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3277 static void trw_layer_new_track ( gpointer lav[2] )
3279 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3281 if ( ! vtl->current_track ) {
3282 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3283 new_track_create_common ( vtl, name );
3286 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3290 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3292 vtl->current_track = vik_track_new();
3293 vtl->current_track->visible = TRUE;
3294 vtl->current_track->is_route = TRUE;
3295 // By default make all routes red
3296 vtl->current_track->has_color = TRUE;
3297 gdk_color_parse ( "red", &vtl->current_track->color );
3298 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3301 static void trw_layer_new_route ( gpointer lav[2] )
3303 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3305 if ( ! vtl->current_track ) {
3306 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3307 new_route_create_common ( vtl, name );
3309 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3313 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3315 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3316 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3318 if ( g_hash_table_size (vtl->routes) > 0 ) {
3319 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3320 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3321 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3322 vik_layers_panel_emit_update ( vlp );
3327 static void trw_layer_finish_track ( gpointer lav[2] )
3329 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3330 vtl->current_track = NULL;
3331 vik_layer_emit_update ( VIK_LAYER(vtl) );
3334 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3336 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3337 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3339 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3340 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3341 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3342 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3343 vik_layers_panel_emit_update ( vlp );
3347 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3349 /* NB do not care if wp is visible or not */
3350 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3353 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3355 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3356 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3358 /* Only 1 waypoint - jump straight to it */
3359 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3360 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3361 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3363 /* If at least 2 waypoints - find center and then zoom to fit */
3364 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3366 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3367 maxmin[0].lat = vtl->waypoints_bbox.north;
3368 maxmin[1].lat = vtl->waypoints_bbox.south;
3369 maxmin[0].lon = vtl->waypoints_bbox.east;
3370 maxmin[1].lon = vtl->waypoints_bbox.west;
3371 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3374 vik_layers_panel_emit_update ( vlp );
3377 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3379 static gpointer pass_along[2];
3381 GtkWidget *export_submenu;
3382 pass_along[0] = vtl;
3383 pass_along[1] = vlp;
3385 item = gtk_menu_item_new();
3386 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3387 gtk_widget_show ( item );
3389 if ( vtl->current_track ) {
3390 if ( vtl->current_track->is_route )
3391 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3393 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3395 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3396 gtk_widget_show ( item );
3399 item = gtk_menu_item_new ();
3400 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3401 gtk_widget_show ( item );
3404 /* Now with icons */
3405 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3406 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3407 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3408 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3409 gtk_widget_show ( item );
3411 GtkWidget *view_submenu = gtk_menu_new();
3412 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3413 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3414 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3415 gtk_widget_show ( item );
3416 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3418 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3420 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3421 gtk_widget_show ( item );
3423 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3424 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3425 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3426 gtk_widget_show ( item );
3428 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3429 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3430 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3431 gtk_widget_show ( item );
3433 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3434 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3436 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3437 gtk_widget_show ( item );
3439 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3440 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3441 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3442 gtk_widget_show ( item );
3444 export_submenu = gtk_menu_new ();
3445 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3446 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3447 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3448 gtk_widget_show ( item );
3449 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3451 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3453 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3454 gtk_widget_show ( item );
3456 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3458 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3459 gtk_widget_show ( item );
3461 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3462 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3463 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3464 gtk_widget_show ( item );
3466 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3467 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3468 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3469 gtk_widget_show ( item );
3471 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3472 item = gtk_menu_item_new_with_mnemonic ( external1 );
3473 g_free ( external1 );
3474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3475 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3476 gtk_widget_show ( item );
3478 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3479 item = gtk_menu_item_new_with_mnemonic ( external2 );
3480 g_free ( external2 );
3481 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3482 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3483 gtk_widget_show ( item );
3485 GtkWidget *new_submenu = gtk_menu_new();
3486 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3488 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3489 gtk_widget_show(item);
3490 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3492 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3495 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3496 gtk_widget_show ( item );
3498 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3499 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3500 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3501 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3502 gtk_widget_show ( item );
3503 // Make it available only when a new track *not* already in progress
3504 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3506 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3507 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3509 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3510 gtk_widget_show ( item );
3511 // Make it available only when a new track *not* already in progress
3512 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3514 #ifdef VIK_CONFIG_GEOTAG
3515 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3516 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3517 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3518 gtk_widget_show ( item );
3521 GtkWidget *acquire_submenu = gtk_menu_new ();
3522 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3523 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3524 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3525 gtk_widget_show ( item );
3526 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3528 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3530 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3531 gtk_widget_show ( item );
3533 /* FIXME: only add menu when at least a routing engine has support for Directions */
3534 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3535 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3536 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3537 gtk_widget_show ( item );
3539 #ifdef VIK_CONFIG_OPENSTREETMAP
3540 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3541 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3542 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3543 gtk_widget_show ( item );
3545 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3546 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3547 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3548 gtk_widget_show ( item );
3551 #ifdef VIK_CONFIG_GEONAMES
3552 GtkWidget *wikipedia_submenu = gtk_menu_new();
3553 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3555 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3556 gtk_widget_show(item);
3557 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3559 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3560 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3562 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3563 gtk_widget_show ( item );
3565 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3566 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3568 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3569 gtk_widget_show ( item );
3572 #ifdef VIK_CONFIG_GEOCACHES
3573 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3574 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3575 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3576 gtk_widget_show ( item );
3579 #ifdef VIK_CONFIG_GEOTAG
3580 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3581 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3582 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3583 gtk_widget_show ( item );
3586 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3588 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3589 gtk_widget_show ( item );
3591 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3593 GtkWidget *upload_submenu = gtk_menu_new ();
3594 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3595 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3596 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3597 gtk_widget_show ( item );
3598 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3600 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3601 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3602 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3603 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3604 gtk_widget_show ( item );
3606 #ifdef VIK_CONFIG_OPENSTREETMAP
3607 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3608 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3609 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3610 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3611 gtk_widget_show ( item );
3614 GtkWidget *delete_submenu = gtk_menu_new ();
3615 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3616 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3617 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3618 gtk_widget_show ( item );
3619 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3621 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3622 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3623 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3624 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3625 gtk_widget_show ( item );
3627 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3628 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3630 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3631 gtk_widget_show ( item );
3633 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3634 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3635 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3636 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3637 gtk_widget_show ( item );
3639 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3640 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3641 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3642 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3643 gtk_widget_show ( item );
3645 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3646 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3647 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3648 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3649 gtk_widget_show ( item );
3651 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3652 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3653 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3654 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3655 gtk_widget_show ( item );
3657 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3658 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3660 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3661 gtk_widget_show ( item );
3664 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3665 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3667 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3668 gtk_widget_show ( item );
3672 // Fake Waypoint UUIDs vi simple increasing integer
3673 static guint wp_uuid = 0;
3675 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3679 vik_waypoint_set_name (wp, name);
3681 if ( VIK_LAYER(vtl)->realized )
3683 // Do we need to create the sublayer:
3684 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3685 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3688 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3690 // Visibility column always needed for waypoints
3691 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 );
3693 // Actual setting of visibility dependent on the waypoint
3694 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3696 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3698 // Sort now as post_read is not called on a realized waypoint
3699 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
3702 highest_wp_number_add_wp(vtl, name);
3703 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3707 // Fake Track UUIDs vi simple increasing integer
3708 static guint tr_uuid = 0;
3710 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3714 vik_track_set_name (t, name);
3716 if ( VIK_LAYER(vtl)->realized )
3718 // Do we need to create the sublayer:
3719 if ( g_hash_table_size (vtl->tracks) == 0 ) {
3720 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3723 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3724 // Visibility column always needed for tracks
3725 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 );
3727 // Actual setting of visibility dependent on the track
3728 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3730 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3732 // Sort now as post_read is not called on a realized track
3733 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
3736 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3738 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3741 // Fake Route UUIDs vi simple increasing integer
3742 static guint rt_uuid = 0;
3744 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3748 vik_track_set_name (t, name);
3750 if ( VIK_LAYER(vtl)->realized )
3752 // Do we need to create the sublayer:
3753 if ( g_hash_table_size (vtl->routes) == 0 ) {
3754 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3757 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3758 // Visibility column always needed for routes
3759 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 );
3760 // Actual setting of visibility dependent on the route
3761 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3763 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3765 // Sort now as post_read is not called on a realized route
3766 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
3769 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3771 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3774 /* to be called whenever a track has been deleted or may have been changed. */
3775 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3777 if (vtl->current_tp_track == trk )
3778 trw_layer_cancel_current_tp ( vtl, FALSE );
3782 * Normally this is done to due the waypoint size preference having changed
3784 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3786 GHashTableIter iter;
3787 gpointer key, value;
3790 g_hash_table_iter_init ( &iter, vtl->waypoints );
3791 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3792 VikWaypoint *wp = VIK_WAYPOINT(value);
3794 // Reapply symbol setting to update the pixbuf
3795 gchar *tmp_symbol = g_strdup ( wp->symbol );
3796 vik_waypoint_set_symbol ( wp, tmp_symbol );
3797 g_free ( tmp_symbol );
3803 * trw_layer_new_unique_sublayer_name:
3805 * Allocates a unique new name
3807 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3810 gchar *newname = g_strdup(name);
3815 switch ( sublayer_type ) {
3816 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3817 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3819 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3820 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3823 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3826 // If found a name already in use try adding 1 to it and we try again
3828 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3830 newname = new_newname;
3833 } while ( id != NULL);
3838 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3840 // No more uniqueness of name forced when loading from a file
3841 // This now makes this function a little redunant as we just flow the parameters through
3842 vik_trw_layer_add_waypoint ( vtl, name, wp );
3845 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3847 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3848 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3849 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3850 vik_track_free ( tr );
3851 vtl->route_finder_append = FALSE; /* this means we have added it */
3854 // No more uniqueness of name forced when loading from a file
3856 vik_trw_layer_add_route ( vtl, name, tr );
3858 vik_trw_layer_add_track ( vtl, name, tr );
3860 if ( vtl->route_finder_check_added_track ) {
3861 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3862 vtl->route_finder_added_track = tr;
3867 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3869 *l = g_list_append(*l, id);
3873 * Move an item from one TRW layer to another TRW layer
3875 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3877 // TODO reconsider strategy when moving within layer (if anything...)
3878 gboolean rename = ( vtl_src != vtl_dest );
3882 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3883 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3887 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3889 newname = g_strdup ( trk->name );
3891 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3892 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3894 vik_trw_layer_delete_track ( vtl_src, trk );
3897 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3898 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3902 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3904 newname = g_strdup ( trk->name );
3906 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3907 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3909 vik_trw_layer_delete_route ( vtl_src, trk );
3912 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3913 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3917 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
3919 newname = g_strdup ( wp->name );
3921 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3922 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3924 trw_layer_delete_waypoint ( vtl_src, wp );
3926 // Recalculate bounds even if not renamed as maybe dragged between layers
3927 trw_layer_calculate_bounds_waypoints ( vtl_dest );
3928 trw_layer_calculate_bounds_waypoints ( vtl_src );
3932 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3934 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3935 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3937 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3938 GList *items = NULL;
3941 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3942 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3944 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3945 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3947 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3948 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3953 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3954 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3955 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3956 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3958 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3965 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3966 trw_layer_move_item(vtl_src, vtl_dest, name, type);
3971 VikTrack *trk; // input
3972 gpointer uuid; // output
3975 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3977 trku_udata *user_data = udata;
3978 if ( trk == user_data->trk ) {
3979 user_data->uuid = id;
3985 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3987 gboolean was_visible = FALSE;
3989 if ( trk && trk->name ) {
3991 if ( trk == vtl->current_track ) {
3992 vtl->current_track = NULL;
3993 vtl->current_tp_track = NULL;
3994 vtl->current_tp_id = NULL;
3995 vtl->moving_tp = FALSE;
3998 was_visible = trk->visible;
4000 if ( trk == vtl->route_finder_current_track )
4001 vtl->route_finder_current_track = NULL;
4003 if ( trk == vtl->route_finder_added_track )
4004 vtl->route_finder_added_track = NULL;
4010 // Hmmm, want key of it
4011 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4013 if ( trkf && udata.uuid ) {
4014 /* could be current_tp, so we have to check */
4015 trw_layer_cancel_tps_of_track ( vtl, trk );
4017 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4020 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4021 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4022 g_hash_table_remove ( vtl->tracks, udata.uuid );
4024 // If last sublayer, then remove sublayer container
4025 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4026 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4034 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4036 gboolean was_visible = FALSE;
4038 if ( trk && trk->name ) {
4040 if ( trk == vtl->current_track ) {
4041 vtl->current_track = NULL;
4042 vtl->current_tp_track = NULL;
4043 vtl->current_tp_id = NULL;
4044 vtl->moving_tp = FALSE;
4047 was_visible = trk->visible;
4049 if ( trk == vtl->route_finder_current_track )
4050 vtl->route_finder_current_track = NULL;
4052 if ( trk == vtl->route_finder_added_track )
4053 vtl->route_finder_added_track = NULL;
4059 // Hmmm, want key of it
4060 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4062 if ( trkf && udata.uuid ) {
4063 /* could be current_tp, so we have to check */
4064 trw_layer_cancel_tps_of_track ( vtl, trk );
4066 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4069 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4070 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4071 g_hash_table_remove ( vtl->routes, udata.uuid );
4073 // If last sublayer, then remove sublayer container
4074 if ( g_hash_table_size (vtl->routes) == 0 ) {
4075 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4083 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4085 gboolean was_visible = FALSE;
4087 if ( wp && wp->name ) {
4089 if ( wp == vtl->current_wp ) {
4090 vtl->current_wp = NULL;
4091 vtl->current_wp_id = NULL;
4092 vtl->moving_wp = FALSE;
4095 was_visible = wp->visible;
4101 // Hmmm, want key of it
4102 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4104 if ( wpf && udata.uuid ) {
4105 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4108 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4109 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4111 highest_wp_number_remove_wp(vtl, wp->name);
4112 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4114 // If last sublayer, then remove sublayer container
4115 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4116 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4126 // Only for temporary use by trw_layer_delete_waypoint_by_name
4127 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4129 wpu_udata *user_data = udata;
4130 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4131 user_data->uuid = id;
4138 * Delete a waypoint by the given name
4139 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4140 * as there be multiple waypoints with the same name
4142 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4145 // Fake a waypoint with the given name
4146 udata.wp = vik_waypoint_new ();
4147 vik_waypoint_set_name (udata.wp, name);
4148 // Currently only the name is used in this waypoint find function
4151 // Hmmm, want key of it
4152 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4154 vik_waypoint_free (udata.wp);
4156 if ( wpf && udata.uuid )
4157 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4163 VikTrack *trk; // input
4164 gpointer uuid; // output
4167 // Only for temporary use by trw_layer_delete_track_by_name
4168 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4170 tpu_udata *user_data = udata;
4171 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4172 user_data->uuid = id;
4179 * Delete a track by the given name
4180 * NOTE: ATM this will delete the first encountered Track with the specified name
4181 * as there may be multiple tracks with the same name within the specified hash table
4183 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4186 // Fake a track with the given name
4187 udata.trk = vik_track_new ();
4188 vik_track_set_name (udata.trk, name);
4189 // Currently only the name is used in this waypoint find function
4192 // Hmmm, want key of it
4193 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4195 vik_track_free (udata.trk);
4197 if ( trkf && udata.uuid ) {
4198 // This could be a little better written...
4199 if ( vtl->tracks == ht_tracks )
4200 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4201 if ( vtl->routes == ht_tracks )
4202 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4209 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4211 vik_treeview_item_delete (vt, it );
4214 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4217 vtl->current_track = NULL;
4218 vtl->route_finder_current_track = NULL;
4219 vtl->route_finder_added_track = NULL;
4220 if (vtl->current_tp_track)
4221 trw_layer_cancel_current_tp(vtl, FALSE);
4223 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4224 g_hash_table_remove_all(vtl->routes_iters);
4225 g_hash_table_remove_all(vtl->routes);
4227 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4229 vik_layer_emit_update ( VIK_LAYER(vtl) );
4232 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4235 vtl->current_track = NULL;
4236 vtl->route_finder_current_track = NULL;
4237 vtl->route_finder_added_track = NULL;
4238 if (vtl->current_tp_track)
4239 trw_layer_cancel_current_tp(vtl, FALSE);
4241 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4242 g_hash_table_remove_all(vtl->tracks_iters);
4243 g_hash_table_remove_all(vtl->tracks);
4245 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4247 vik_layer_emit_update ( VIK_LAYER(vtl) );
4250 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4252 vtl->current_wp = NULL;
4253 vtl->current_wp_id = NULL;
4254 vtl->moving_wp = FALSE;
4256 highest_wp_number_reset(vtl);
4258 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4259 g_hash_table_remove_all(vtl->waypoints_iters);
4260 g_hash_table_remove_all(vtl->waypoints);
4262 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4264 vik_layer_emit_update ( VIK_LAYER(vtl) );
4267 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4269 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4270 // Get confirmation from the user
4271 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4272 _("Are you sure you want to delete all tracks in %s?"),
4273 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4274 vik_trw_layer_delete_all_tracks (vtl);
4277 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4279 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4280 // Get confirmation from the user
4281 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4282 _("Are you sure you want to delete all routes in %s?"),
4283 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4284 vik_trw_layer_delete_all_routes (vtl);
4287 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4289 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4290 // Get confirmation from the user
4291 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4292 _("Are you sure you want to delete all waypoints in %s?"),
4293 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4294 vik_trw_layer_delete_all_waypoints (vtl);
4297 static void trw_layer_delete_item ( gpointer pass_along[6] )
4299 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4300 gboolean was_visible = FALSE;
4301 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4303 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4304 if ( wp && wp->name ) {
4305 if ( GPOINTER_TO_INT ( pass_along[4]) )
4306 // Get confirmation from the user
4307 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4308 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4309 _("Are you sure you want to delete the waypoint \"%s\""),
4312 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4315 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4317 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4318 if ( trk && trk->name ) {
4319 if ( GPOINTER_TO_INT ( pass_along[4]) )
4320 // Get confirmation from the user
4321 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4322 _("Are you sure you want to delete the track \"%s\""),
4325 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4330 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4331 if ( trk && trk->name ) {
4332 if ( GPOINTER_TO_INT ( pass_along[4]) )
4333 // Get confirmation from the user
4334 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4335 _("Are you sure you want to delete the route \"%s\""),
4338 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4342 vik_layer_emit_update ( VIK_LAYER(vtl) );
4346 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4348 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4350 vik_waypoint_set_name ( wp, new_name );
4352 // Now update the treeview as well
4357 // Need key of it for treeview update
4358 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4360 if ( wpf && udataU.uuid ) {
4361 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4364 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4365 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4370 static void trw_layer_properties_item ( gpointer pass_along[7] )
4372 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4373 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4375 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4377 if ( wp && wp->name )
4379 gboolean updated = FALSE;
4380 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4382 trw_layer_waypoint_rename ( vtl, wp, new_name );
4384 if ( updated && pass_along[6] )
4385 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4387 if ( updated && VIK_LAYER(vtl)->visible )
4388 vik_layer_emit_update ( VIK_LAYER(vtl) );
4394 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4395 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4397 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4399 if ( tr && tr->name )
4401 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4404 pass_along[1], /* vlp */
4405 pass_along[5], /* vvp */
4406 pass_along[6]); /* iter */
4412 * Update the treeview of the track id - primarily to update the icon
4414 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4420 gpointer *trkf = NULL;
4421 if ( trk->is_route )
4422 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4424 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4426 if ( trkf && udata.uuid ) {
4428 GtkTreeIter *iter = NULL;
4429 if ( trk->is_route )
4430 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4432 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4435 // TODO: Make this a function
4436 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4437 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4438 ((trk->color.green & 0xff00) << 8) |
4439 (trk->color.blue & 0xff00);
4440 gdk_pixbuf_fill ( pixbuf, pixel );
4441 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4442 g_object_unref (pixbuf);
4449 Parameter 1 -> VikLayersPanel
4450 Parameter 2 -> VikLayer
4451 Parameter 3 -> VikViewport
4453 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4456 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4457 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4460 /* since vlp not set, vl & vvp should be valid instead! */
4462 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4463 vik_layer_emit_update ( VIK_LAYER(vl) );
4468 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4470 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4472 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4473 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4475 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4477 if ( track && track->trackpoints )
4478 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4481 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4483 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4485 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4486 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4488 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4490 if ( track && track->trackpoints )
4492 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4494 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4495 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4496 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4497 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4498 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4502 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4504 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4506 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4507 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4509 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4514 // Converting a track to a route can be a bit more complicated,
4515 // so give a chance to change our minds:
4516 if ( !trk->is_route &&
4517 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4518 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4520 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4521 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4526 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4529 trk_copy->is_route = !trk_copy->is_route;
4531 // ATM can't set name to self - so must create temporary copy
4532 gchar *name = g_strdup ( trk_copy->name );
4534 // Delete old one and then add new one
4535 if ( trk->is_route ) {
4536 vik_trw_layer_delete_route ( vtl, trk );
4537 vik_trw_layer_add_track ( vtl, name, trk_copy );
4540 // Extra route conversion bits...
4541 vik_track_merge_segments ( trk_copy );
4542 vik_track_to_routepoints ( trk_copy );
4544 vik_trw_layer_delete_track ( vtl, trk );
4545 vik_trw_layer_add_route ( vtl, name, trk_copy );
4549 // Update in case color of track / route changes when moving between sublayers
4550 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4554 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4556 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4558 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4559 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4561 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4566 vtl->current_track = track;
4567 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);
4569 if ( track->trackpoints )
4570 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4574 * extend a track using route finder
4576 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4578 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4579 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4582 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4584 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4585 vtl->route_finder_coord = last_coord;
4586 vtl->route_finder_current_track = track;
4587 vtl->route_finder_started = TRUE;
4589 if ( track->trackpoints )
4590 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4594 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4596 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4597 /* Also warn if overwrite old elevation data */
4598 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4600 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4601 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4603 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4606 vik_track_apply_dem_data ( track );
4609 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4611 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4613 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4614 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4616 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4621 GList *trps = track->trackpoints;
4624 trps = g_list_last(trps);
4625 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4628 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4630 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4632 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4633 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4635 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4640 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4643 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4646 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4648 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4650 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4651 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4653 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4658 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4661 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4664 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4666 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4668 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4669 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4671 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4676 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4679 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4683 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4685 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4687 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4689 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4690 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4692 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4694 if ( trk && trk->trackpoints )
4696 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4697 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4698 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
4699 if ( pass_along[1] )
4700 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4702 vik_layer_emit_update ( VIK_LAYER(vtl) );
4706 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4708 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4709 trw_layer_tpwin_init ( vtl );
4712 /*************************************
4713 * merge/split by time routines
4714 *************************************/
4716 /* called for each key in track hash table.
4717 * If the current track has the same time stamp type, add it to the result,
4718 * except the one pointed by "exclude".
4719 * set exclude to NULL if there is no exclude to check.
4720 * Note that the result is in reverse (for performance reasons).
4725 gboolean with_timestamps;
4727 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4729 twt_udata *user_data = udata;
4730 VikTrackpoint *p1, *p2;
4732 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4736 if (VIK_TRACK(value)->trackpoints) {
4737 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4738 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4740 if ( user_data->with_timestamps ) {
4741 if (!p1->has_timestamp || !p2->has_timestamp) {
4746 // Don't add tracks with timestamps when getting non timestamp tracks
4747 if (p1->has_timestamp || p2->has_timestamp) {
4753 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4756 /* called for each key in track hash table. if original track user_data[1] is close enough
4757 * to the passed one, add it to list in user_data[0]
4759 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4762 VikTrackpoint *p1, *p2;
4763 VikTrack *trk = VIK_TRACK(value);
4765 GList **nearby_tracks = ((gpointer *)user_data)[0];
4766 GList *tpoints = ((gpointer *)user_data)[1];
4769 * detect reasons for not merging, and return
4770 * if no reason is found not to merge, then do it.
4773 // Exclude the original track from the compiled list
4774 if (trk->trackpoints == tpoints) {
4778 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4779 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4781 if (trk->trackpoints) {
4782 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4783 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4785 if (!p1->has_timestamp || !p2->has_timestamp) {
4786 //g_print("no timestamp\n");
4790 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4791 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4792 if (! (abs(t1 - p2->timestamp) < threshold ||
4794 abs(p1->timestamp - t2) < threshold)
4801 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4804 /* comparison function used to sort tracks; a and b are hash table keys */
4805 /* Not actively used - can be restored if needed
4806 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4808 GHashTable *tracks = user_data;
4811 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4812 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4814 if (t1 < t2) return -1;
4815 if (t1 > t2) return 1;
4820 /* comparison function used to sort trackpoints */
4821 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4823 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4825 if (t1 < t2) return -1;
4826 if (t1 > t2) return 1;
4831 * comparison function which can be used to sort tracks or waypoints by name
4833 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4835 const gchar* namea = (const gchar*) a;
4836 const gchar* nameb = (const gchar*) b;
4837 if ( namea == NULL || nameb == NULL)
4840 // Same sort method as used in the vik_treeview_*_alphabetize functions
4841 return strcmp ( namea, nameb );
4845 * Attempt to merge selected track with other tracks specified by the user
4846 * Tracks to merge with must be of the same 'type' as the selected track -
4847 * either all with timestamps, or all without timestamps
4849 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4851 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4852 GList *other_tracks = NULL;
4853 GHashTable *ght_tracks;
4854 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4855 ght_tracks = vtl->routes;
4857 ght_tracks = vtl->tracks;
4859 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4864 if ( !track->trackpoints )
4868 udata.result = &other_tracks;
4869 udata.exclude = track->trackpoints;
4870 // Allow merging with 'similar' time type time tracks
4871 // i.e. either those times, or those without
4872 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4874 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4875 other_tracks = g_list_reverse(other_tracks);
4877 if ( !other_tracks ) {
4878 if ( udata.with_timestamps )
4879 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4881 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4885 // Sort alphabetically for user presentation
4886 // Convert into list of names for usage with dialog function
4887 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4888 GList *other_tracks_names = NULL;
4889 GList *iter = g_list_first ( other_tracks );
4891 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4892 iter = g_list_next ( iter );
4895 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4897 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4901 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4902 g_list_free(other_tracks);
4903 g_list_free(other_tracks_names);
4908 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4909 VikTrack *merge_track;
4910 if ( track->is_route )
4911 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4913 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4916 vik_track_steal_and_append_trackpoints ( track, merge_track );
4917 if ( track->is_route )
4918 vik_trw_layer_delete_route (vtl, merge_track);
4920 vik_trw_layer_delete_track (vtl, merge_track);
4921 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4924 for (l = merge_list; l != NULL; l = g_list_next(l))
4926 g_list_free(merge_list);
4928 vik_layer_emit_update( VIK_LAYER(vtl) );
4932 // c.f. trw_layer_sorted_track_id_by_name_list
4933 // but don't add the specified track to the list (normally current track)
4934 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4936 twt_udata *user_data = udata;
4939 if (trk->trackpoints == user_data->exclude) {
4943 // Sort named list alphabetically
4944 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4948 * Join - this allows combining 'tracks' and 'track routes'
4949 * i.e. doesn't care about whether tracks have consistent timestamps
4950 * ATM can only append one track at a time to the currently selected track
4952 static void trw_layer_append_track ( gpointer pass_along[6] )
4955 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4957 GHashTable *ght_tracks;
4958 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4959 ght_tracks = vtl->routes;
4961 ght_tracks = vtl->tracks;
4963 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4968 GList *other_tracks_names = NULL;
4970 // Sort alphabetically for user presentation
4971 // Convert into list of names for usage with dialog function
4972 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4974 udata.result = &other_tracks_names;
4975 udata.exclude = trk->trackpoints;
4977 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4979 // Note the limit to selecting one track only
4980 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4981 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4982 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4985 trk->is_route ? _("Append Route"): _("Append Track"),
4986 trk->is_route ? _("Select the route to append after the current route") :
4987 _("Select the track to append after the current track") );
4989 g_list_free(other_tracks_names);
4991 // It's a list, but shouldn't contain more than one other track!
4992 if ( append_list ) {
4994 for (l = append_list; l != NULL; l = g_list_next(l)) {
4995 // TODO: at present this uses the first track found by name,
4996 // which with potential multiple same named tracks may not be the one selected...
4997 VikTrack *append_track;
4998 if ( trk->is_route )
4999 append_track = vik_trw_layer_get_route ( vtl, l->data );
5001 append_track = vik_trw_layer_get_track ( vtl, l->data );
5003 if ( append_track ) {
5004 vik_track_steal_and_append_trackpoints ( trk, append_track );
5005 if ( trk->is_route )
5006 vik_trw_layer_delete_route (vtl, append_track);
5008 vik_trw_layer_delete_track (vtl, append_track);
5011 for (l = append_list; l != NULL; l = g_list_next(l))
5013 g_list_free(append_list);
5015 vik_layer_emit_update( VIK_LAYER(vtl) );
5020 * Very similar to trw_layer_append_track for joining
5021 * but this allows selection from the 'other' list
5022 * If a track is selected, then is shows routes and joins the selected one
5023 * If a route is selected, then is shows tracks and joins the selected one
5025 static void trw_layer_append_other ( gpointer pass_along[6] )
5028 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5030 GHashTable *ght_mykind, *ght_others;
5031 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5032 ght_mykind = vtl->routes;
5033 ght_others = vtl->tracks;
5036 ght_mykind = vtl->tracks;
5037 ght_others = vtl->routes;
5040 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5045 GList *other_tracks_names = NULL;
5047 // Sort alphabetically for user presentation
5048 // Convert into list of names for usage with dialog function
5049 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5051 udata.result = &other_tracks_names;
5052 udata.exclude = trk->trackpoints;
5054 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5056 // Note the limit to selecting one track only
5057 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5058 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5059 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5062 trk->is_route ? _("Append Track"): _("Append Route"),
5063 trk->is_route ? _("Select the track to append after the current route") :
5064 _("Select the route to append after the current track") );
5066 g_list_free(other_tracks_names);
5068 // It's a list, but shouldn't contain more than one other track!
5069 if ( append_list ) {
5071 for (l = append_list; l != NULL; l = g_list_next(l)) {
5072 // TODO: at present this uses the first track found by name,
5073 // which with potential multiple same named tracks may not be the one selected...
5075 // Get FROM THE OTHER TYPE list
5076 VikTrack *append_track;
5077 if ( trk->is_route )
5078 append_track = vik_trw_layer_get_track ( vtl, l->data );
5080 append_track = vik_trw_layer_get_route ( vtl, l->data );
5082 if ( append_track ) {
5084 if ( !append_track->is_route &&
5085 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5086 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5088 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5089 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5090 vik_track_merge_segments ( append_track );
5091 vik_track_to_routepoints ( append_track );
5098 vik_track_steal_and_append_trackpoints ( trk, append_track );
5100 // Delete copied which is FROM THE OTHER TYPE list
5101 if ( trk->is_route )
5102 vik_trw_layer_delete_track (vtl, append_track);
5104 vik_trw_layer_delete_route (vtl, append_track);
5107 for (l = append_list; l != NULL; l = g_list_next(l))
5109 g_list_free(append_list);
5110 vik_layer_emit_update( VIK_LAYER(vtl) );
5114 /* merge by segments */
5115 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5117 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5118 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5119 guint segments = vik_track_merge_segments ( trk );
5120 // NB currently no need to redraw as segments not actually shown on the display
5121 // However inform the user of what happened:
5123 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5124 g_snprintf(str, 64, tmp_str, segments);
5125 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5128 /* merge by time routine */
5129 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5131 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5135 GList *tracks_with_timestamp = NULL;
5136 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5137 if (orig_trk->trackpoints &&
5138 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5139 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5144 udata.result = &tracks_with_timestamp;
5145 udata.exclude = orig_trk->trackpoints;
5146 udata.with_timestamps = TRUE;
5147 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5148 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5150 if (!tracks_with_timestamp) {
5151 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5154 g_list_free(tracks_with_timestamp);
5156 static guint threshold_in_minutes = 1;
5157 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5158 _("Merge Threshold..."),
5159 _("Merge when time between tracks less than:"),
5160 &threshold_in_minutes)) {
5164 // keep attempting to merge all tracks until no merges within the time specified is possible
5165 gboolean attempt_merge = TRUE;
5166 GList *nearby_tracks = NULL;
5168 static gpointer params[3];
5170 while ( attempt_merge ) {
5172 // Don't try again unless tracks have changed
5173 attempt_merge = FALSE;
5175 trps = orig_trk->trackpoints;
5179 if (nearby_tracks) {
5180 g_list_free(nearby_tracks);
5181 nearby_tracks = NULL;
5184 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5185 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5187 /* g_print("Original track times: %d and %d\n", t1, t2); */
5188 params[0] = &nearby_tracks;
5189 params[1] = (gpointer)trps;
5190 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5192 /* get a list of adjacent-in-time tracks */
5193 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5196 GList *l = nearby_tracks;
5199 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5200 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5202 t1 = get_first_trackpoint(l)->timestamp;
5203 t2 = get_last_trackpoint(l)->timestamp;
5204 #undef get_first_trackpoint
5205 #undef get_last_trackpoint
5206 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5209 /* remove trackpoints from merged track, delete track */
5210 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5211 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5213 // Tracks have changed, therefore retry again against all the remaining tracks
5214 attempt_merge = TRUE;
5219 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5222 g_list_free(nearby_tracks);
5224 vik_layer_emit_update( VIK_LAYER(vtl) );
5228 * Split a track at the currently selected trackpoint
5230 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5232 if ( !vtl->current_tpl )
5235 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5236 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5238 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5239 GList *newglist = g_list_alloc ();
5240 newglist->prev = NULL;
5241 newglist->next = vtl->current_tpl->next;
5242 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5243 tr->trackpoints = newglist;
5245 vtl->current_tpl->next->prev = newglist; /* end old track here */
5246 vtl->current_tpl->next = NULL;
5248 // Bounds of the selected track changed due to the split
5249 vik_track_calculate_bounds ( vtl->current_tp_track );
5251 vtl->current_tpl = newglist; /* change tp to first of new track. */
5252 vtl->current_tp_track = tr;
5255 vik_trw_layer_add_route ( vtl, name, tr );
5257 vik_trw_layer_add_track ( vtl, name, tr );
5259 // Bounds of the new track created by the split
5260 vik_track_calculate_bounds ( tr );
5266 // Also need id of newly created track
5269 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5271 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5273 if ( trkf && udata.uuid )
5274 vtl->current_tp_id = udata.uuid;
5276 vtl->current_tp_id = NULL;
5278 vik_layer_emit_update(VIK_LAYER(vtl));
5284 /* split by time routine */
5285 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5287 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5288 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5289 GList *trps = track->trackpoints;
5291 GList *newlists = NULL;
5292 GList *newtps = NULL;
5293 static guint thr = 1;
5300 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5301 _("Split Threshold..."),
5302 _("Split when time between trackpoints exceeds:"),
5307 /* iterate through trackpoints, and copy them into new lists without touching original list */
5308 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5312 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5314 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5317 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5318 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5319 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5321 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5326 if (ts - prev_ts > thr*60) {
5327 /* flush accumulated trackpoints into new list */
5328 newlists = g_list_append(newlists, g_list_reverse(newtps));
5332 /* accumulate trackpoint copies in newtps, in reverse order */
5333 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5335 iter = g_list_next(iter);
5338 newlists = g_list_append(newlists, g_list_reverse(newtps));
5341 /* put lists of trackpoints into tracks */
5343 // Only bother updating if the split results in new tracks
5344 if (g_list_length (newlists) > 1) {
5349 tr = vik_track_copy ( track, FALSE );
5350 tr->trackpoints = (GList *)(iter->data);
5352 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5353 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5354 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5355 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5356 g_free ( new_tr_name );
5357 vik_track_calculate_bounds ( tr );
5358 iter = g_list_next(iter);
5360 // Remove original track and then update the display
5361 vik_trw_layer_delete_track (vtl, track);
5362 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5364 g_list_free(newlists);
5368 * Split a track by the number of points as specified by the user
5370 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5372 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5374 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5375 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5377 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5382 // Check valid track
5383 GList *trps = track->trackpoints;
5387 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5388 _("Split Every Nth Point"),
5389 _("Split on every Nth point:"),
5390 250, // Default value as per typical limited track capacity of various GPS devices
5394 // Was a valid number returned?
5400 GList *newlists = NULL;
5401 GList *newtps = NULL;
5406 /* accumulate trackpoint copies in newtps, in reverse order */
5407 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5409 if (count >= points) {
5410 /* flush accumulated trackpoints into new list */
5411 newlists = g_list_append(newlists, g_list_reverse(newtps));
5415 iter = g_list_next(iter);
5418 // If there is a remaining chunk put that into the new split list
5419 // This may well be the whole track if no split points were encountered
5421 newlists = g_list_append(newlists, g_list_reverse(newtps));
5424 /* put lists of trackpoints into tracks */
5426 // Only bother updating if the split results in new tracks
5427 if (g_list_length (newlists) > 1) {
5432 tr = vik_track_copy ( track, FALSE );
5433 tr->trackpoints = (GList *)(iter->data);
5435 if ( track->is_route ) {
5436 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5437 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5440 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5441 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5443 g_free ( new_tr_name );
5444 vik_track_calculate_bounds ( tr );
5446 iter = g_list_next(iter);
5448 // Remove original track and then update the display
5449 if ( track->is_route )
5450 vik_trw_layer_delete_route (vtl, track);
5452 vik_trw_layer_delete_track (vtl, track);
5453 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5455 g_list_free(newlists);
5459 * Split a track at the currently selected trackpoint
5461 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5463 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5464 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5465 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5469 * Split a track by its segments
5470 * Routes do not have segments so don't call this for routes
5472 static void trw_layer_split_segments ( gpointer pass_along[6] )
5474 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5475 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5482 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5485 for ( i = 0; i < ntracks; i++ ) {
5487 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5488 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5489 g_free ( new_tr_name );
5494 // Remove original track
5495 vik_trw_layer_delete_track ( vtl, trk );
5496 vik_layer_emit_update ( VIK_LAYER(vtl) );
5499 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5502 /* end of split/merge routines */
5505 * Delete adjacent track points at the same position
5506 * AKA Delete Dulplicates on the Properties Window
5508 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5510 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5512 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5513 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5515 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5520 gulong removed = vik_track_remove_dup_points ( trk );
5522 // Track has been updated so update tps:
5523 trw_layer_cancel_tps_of_track ( vtl, trk );
5525 // Inform user how much was deleted as it's not obvious from the normal view
5527 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5528 g_snprintf(str, 64, tmp_str, removed);
5529 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5531 vik_layer_emit_update ( VIK_LAYER(vtl) );
5535 * Delete adjacent track points with the same timestamp
5536 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5538 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5540 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5542 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5543 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5545 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5550 gulong removed = vik_track_remove_same_time_points ( trk );
5552 // Track has been updated so update tps:
5553 trw_layer_cancel_tps_of_track ( vtl, trk );
5555 // Inform user how much was deleted as it's not obvious from the normal view
5557 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5558 g_snprintf(str, 64, tmp_str, removed);
5559 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5561 vik_layer_emit_update ( VIK_LAYER(vtl) );
5567 static void trw_layer_reverse ( gpointer pass_along[6] )
5569 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5571 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5572 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5574 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5579 vik_track_reverse ( track );
5581 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5585 * Similar to trw_layer_enum_item, but this uses a sorted method
5588 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5590 GList **list = (GList**)udata;
5591 // *list = g_list_prepend(*all, key); //unsorted method
5592 // Sort named list alphabetically
5593 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5598 * Now Waypoint specific sort
5600 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5602 GList **list = (GList**)udata;
5603 // Sort named list alphabetically
5604 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5608 * Track specific sort
5610 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5612 GList **list = (GList**)udata;
5613 // Sort named list alphabetically
5614 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5619 gboolean has_same_track_name;
5620 const gchar *same_track_name;
5621 } same_track_name_udata;
5623 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5625 const gchar* namea = (const gchar*) aa;
5626 const gchar* nameb = (const gchar*) bb;
5629 gint result = strcmp ( namea, nameb );
5631 if ( result == 0 ) {
5632 // Found two names the same
5633 same_track_name_udata *user_data = udata;
5634 user_data->has_same_track_name = TRUE;
5635 user_data->same_track_name = namea;
5638 // Leave ordering the same
5643 * Find out if any tracks have the same name in this hash table
5645 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5647 // Sort items by name, then compare if any next to each other are the same
5649 GList *track_names = NULL;
5650 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5653 if ( ! track_names )
5656 same_track_name_udata udata;
5657 udata.has_same_track_name = FALSE;
5659 // Use sort routine to traverse list comparing items
5660 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5661 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5662 // Still no tracks...
5666 return udata.has_same_track_name;
5670 * Force unqiue track names for the track table specified
5671 * Note the panel is a required parameter to enable the update of the names displayed
5672 * Specify if on tracks or else on routes
5674 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5676 // . Search list for an instance of repeated name
5677 // . get track of this name
5678 // . create new name
5679 // . rename track & update equiv. treeview iter
5680 // . repeat until all different
5682 same_track_name_udata udata;
5684 GList *track_names = NULL;
5685 udata.has_same_track_name = FALSE;
5686 udata.same_track_name = NULL;
5688 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5691 if ( ! track_names )
5694 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5696 // Still no tracks...
5697 if ( ! dummy_list1 )
5700 while ( udata.has_same_track_name ) {
5702 // Find a track with the same name
5705 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5707 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5711 g_critical("Houston, we've had a problem.");
5712 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5713 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5718 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5719 vik_track_set_name ( trk, newname );
5725 // Need want key of it for treeview update
5726 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5728 if ( trkf && udataU.uuid ) {
5732 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5734 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5737 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5739 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
5741 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
5745 // Start trying to find same names again...
5747 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5748 udata.has_same_track_name = FALSE;
5749 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5751 // No tracks any more - give up searching
5752 if ( ! dummy_list2 )
5753 udata.has_same_track_name = FALSE;
5757 vik_layers_panel_emit_update ( vlp );
5760 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
5762 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5765 switch (GPOINTER_TO_INT (pass_along[2])) {
5766 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5767 iter = &(vtl->tracks_iter);
5768 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5770 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5771 iter = &(vtl->routes_iter);
5772 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5774 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5775 iter = &(vtl->waypoints_iter);
5776 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5780 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
5783 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
5785 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5788 switch (GPOINTER_TO_INT (pass_along[2])) {
5789 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5790 iter = &(vtl->tracks_iter);
5791 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5793 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5794 iter = &(vtl->routes_iter);
5795 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5797 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5798 iter = &(vtl->waypoints_iter);
5799 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5803 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
5809 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5811 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5814 // Ensure list of track names offered is unique
5815 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5816 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5817 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5818 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5824 // Sort list alphabetically for better presentation
5825 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5828 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5832 // Get list of items to delete from the user
5833 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5836 _("Delete Selection"),
5837 _("Select tracks to delete"));
5840 // Delete requested tracks
5841 // since specificly requested, IMHO no need for extra confirmation
5842 if ( delete_list ) {
5844 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5845 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5846 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5848 g_list_free(delete_list);
5849 vik_layer_emit_update( VIK_LAYER(vtl) );
5856 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5858 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5861 // Ensure list of track names offered is unique
5862 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5863 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5864 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5865 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5871 // Sort list alphabetically for better presentation
5872 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5875 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5879 // Get list of items to delete from the user
5880 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5883 _("Delete Selection"),
5884 _("Select routes to delete") );
5887 // Delete requested routes
5888 // since specificly requested, IMHO no need for extra confirmation
5889 if ( delete_list ) {
5891 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5892 // This deletes first route it finds of that name (but uniqueness is enforced above)
5893 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5895 g_list_free(delete_list);
5896 vik_layer_emit_update( VIK_LAYER(vtl) );
5901 gboolean has_same_waypoint_name;
5902 const gchar *same_waypoint_name;
5903 } same_waypoint_name_udata;
5905 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5907 const gchar* namea = (const gchar*) aa;
5908 const gchar* nameb = (const gchar*) bb;
5911 gint result = strcmp ( namea, nameb );
5913 if ( result == 0 ) {
5914 // Found two names the same
5915 same_waypoint_name_udata *user_data = udata;
5916 user_data->has_same_waypoint_name = TRUE;
5917 user_data->same_waypoint_name = namea;
5920 // Leave ordering the same
5925 * Find out if any waypoints have the same name in this layer
5927 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5929 // Sort items by name, then compare if any next to each other are the same
5931 GList *waypoint_names = NULL;
5932 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5935 if ( ! waypoint_names )
5938 same_waypoint_name_udata udata;
5939 udata.has_same_waypoint_name = FALSE;
5941 // Use sort routine to traverse list comparing items
5942 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5943 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5944 // Still no waypoints...
5948 return udata.has_same_waypoint_name;
5952 * Force unqiue waypoint names for this layer
5953 * Note the panel is a required parameter to enable the update of the names displayed
5955 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5957 // . Search list for an instance of repeated name
5958 // . get waypoint of this name
5959 // . create new name
5960 // . rename waypoint & update equiv. treeview iter
5961 // . repeat until all different
5963 same_waypoint_name_udata udata;
5965 GList *waypoint_names = NULL;
5966 udata.has_same_waypoint_name = FALSE;
5967 udata.same_waypoint_name = NULL;
5969 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5972 if ( ! waypoint_names )
5975 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5977 // Still no waypoints...
5978 if ( ! dummy_list1 )
5981 while ( udata.has_same_waypoint_name ) {
5983 // Find a waypoint with the same name
5984 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5988 g_critical("Houston, we've had a problem.");
5989 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5990 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5995 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5997 trw_layer_waypoint_rename ( vtl, waypoint, newname );
5999 // Start trying to find same names again...
6000 waypoint_names = NULL;
6001 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6002 udata.has_same_waypoint_name = FALSE;
6003 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6005 // No waypoints any more - give up searching
6006 if ( ! dummy_list2 )
6007 udata.has_same_waypoint_name = FALSE;
6011 vik_layers_panel_emit_update ( vlp );
6017 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6019 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6022 // Ensure list of waypoint names offered is unique
6023 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6024 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6025 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6026 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6032 // Sort list alphabetically for better presentation
6033 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6035 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6039 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6041 // Get list of items to delete from the user
6042 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6045 _("Delete Selection"),
6046 _("Select waypoints to delete"));
6049 // Delete requested waypoints
6050 // since specificly requested, IMHO no need for extra confirmation
6051 if ( delete_list ) {
6053 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6054 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6055 trw_layer_delete_waypoint_by_name (vtl, l->data);
6057 g_list_free(delete_list);
6059 trw_layer_calculate_bounds_waypoints ( vtl );
6060 vik_layer_emit_update( VIK_LAYER(vtl) );
6068 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6070 vik_treeview_item_toggle_visible ( vt, it );
6076 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6078 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6084 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6086 wp->visible = GPOINTER_TO_INT (on_off);
6092 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6094 wp->visible = !wp->visible;
6100 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6102 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6103 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6104 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6105 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6107 vik_layer_emit_update ( VIK_LAYER(vtl) );
6113 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6115 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6116 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6117 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6118 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6120 vik_layer_emit_update ( VIK_LAYER(vtl) );
6126 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6128 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6129 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6130 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6132 vik_layer_emit_update ( VIK_LAYER(vtl) );
6138 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6140 trk->visible = GPOINTER_TO_INT (on_off);
6146 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6148 trk->visible = !trk->visible;
6154 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6156 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6157 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6158 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6159 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6161 vik_layer_emit_update ( VIK_LAYER(vtl) );
6167 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6169 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6170 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6171 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6172 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6174 vik_layer_emit_update ( VIK_LAYER(vtl) );
6180 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6182 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6183 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6184 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6186 vik_layer_emit_update ( VIK_LAYER(vtl) );
6192 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6194 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6195 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6196 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6197 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6199 vik_layer_emit_update ( VIK_LAYER(vtl) );
6205 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6207 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6208 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6209 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6210 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6212 vik_layer_emit_update ( VIK_LAYER(vtl) );
6218 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6220 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6221 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6222 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6224 vik_layer_emit_update ( VIK_LAYER(vtl) );
6228 * trw_layer_analyse_close:
6230 * Stuff to do on dialog closure
6232 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6234 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6235 gtk_widget_destroy ( dialog );
6236 vtl->tracks_analysis_dialog = NULL;
6240 * trw_layer_analyse_create_list:
6242 * Create the latest list of tracks with the associated layer(s)
6243 * Although this will always be from a single layer here
6245 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6247 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6248 GList *tracks = NULL;
6249 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6250 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6252 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6254 GList *tracks_and_layers = NULL;
6255 // build tracks_and_layers list
6256 tracks = g_list_first ( tracks );
6258 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6259 vtdl->trk = VIK_TRACK(tracks->data);
6261 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6262 tracks = g_list_next ( tracks );
6265 return tracks_and_layers;
6268 static void trw_layer_tracks_stats ( gpointer lav[2] )
6270 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6271 // There can only be one!
6272 if ( vtl->tracks_analysis_dialog )
6275 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6276 VIK_LAYER(vtl)->name,
6278 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6279 trw_layer_analyse_create_list,
6280 trw_layer_analyse_close );
6286 static void trw_layer_routes_stats ( gpointer lav[2] )
6288 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6289 // There can only be one!
6290 if ( vtl->tracks_analysis_dialog )
6293 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6294 VIK_LAYER(vtl)->name,
6296 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6297 trw_layer_analyse_create_list,
6298 trw_layer_analyse_close );
6301 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6303 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6305 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6308 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6310 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6313 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6314 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6318 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6320 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6323 if ( !strncmp(wp->comment, "http", 4) ) {
6324 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6325 } else if ( !strncmp(wp->description, "http", 4) ) {
6326 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6330 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6332 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6334 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6336 // No actual change to the name supplied
6338 if (strcmp(newname, wp->name) == 0 )
6341 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6344 // An existing waypoint has been found with the requested name
6345 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6346 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6351 // Update WP name and refresh the treeview
6352 vik_waypoint_set_name (wp, newname);
6354 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6355 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6357 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6362 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6364 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6366 // No actual change to the name supplied
6368 if (strcmp(newname, trk->name) == 0)
6371 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6374 // An existing track has been found with the requested name
6375 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6376 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6380 // Update track name and refresh GUI parts
6381 vik_track_set_name (trk, newname);
6383 // Update any subwindows that could be displaying this track which has changed name
6384 // Only one Track Edit Window
6385 if ( l->current_tp_track == trk && l->tpwin ) {
6386 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6388 // Property Dialog of the track
6389 vik_trw_layer_propwin_update ( trk );
6391 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6392 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6394 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6399 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6401 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6403 // No actual change to the name supplied
6405 if (strcmp(newname, trk->name) == 0)
6408 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6411 // An existing track has been found with the requested name
6412 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6413 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6417 // Update track name and refresh GUI parts
6418 vik_track_set_name (trk, newname);
6420 // Update any subwindows that could be displaying this track which has changed name
6421 // Only one Track Edit Window
6422 if ( l->current_tp_track == trk && l->tpwin ) {
6423 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6425 // Property Dialog of the track
6426 vik_trw_layer_propwin_update ( trk );
6428 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6429 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6431 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6438 static gboolean is_valid_geocache_name ( gchar *str )
6440 gint len = strlen ( str );
6441 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]));
6444 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6446 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6447 a_acquire_set_filter_track ( trk );
6450 #ifdef VIK_CONFIG_GOOGLE
6451 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6453 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6454 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6457 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6459 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6461 gchar *escaped = uri_escape ( tr->comment );
6462 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6463 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6470 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6471 /* viewpoint is now available instead */
6472 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6474 static gpointer pass_along[8];
6476 gboolean rv = FALSE;
6479 pass_along[1] = vlp;
6480 pass_along[2] = GINT_TO_POINTER (subtype);
6481 pass_along[3] = sublayer;
6482 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6483 pass_along[5] = vvp;
6484 pass_along[6] = iter;
6485 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6487 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6491 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6493 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6494 gtk_widget_show ( item );
6496 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6497 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6498 if (tr && tr->property_dialog)
6499 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6501 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6502 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6503 if (tr && tr->property_dialog)
6504 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6507 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6508 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6509 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6510 gtk_widget_show ( item );
6512 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6513 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6514 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6515 gtk_widget_show ( item );
6517 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6519 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6520 gtk_widget_show ( item );
6522 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6524 gboolean separator_created = FALSE;
6526 /* could be a right-click using the tool */
6527 if ( vlp != NULL ) {
6528 item = gtk_menu_item_new ();
6529 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6530 gtk_widget_show ( item );
6532 separator_created = TRUE;
6534 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6535 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6537 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6538 gtk_widget_show ( item );
6541 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6543 if ( wp && wp->name ) {
6544 if ( is_valid_geocache_name ( wp->name ) ) {
6546 if ( !separator_created ) {
6547 item = gtk_menu_item_new ();
6548 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6549 gtk_widget_show ( item );
6550 separator_created = TRUE;
6553 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6554 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6555 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6556 gtk_widget_show ( item );
6560 if ( wp && wp->image )
6562 if ( !separator_created ) {
6563 item = gtk_menu_item_new ();
6564 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6565 gtk_widget_show ( item );
6566 separator_created = TRUE;
6569 // Set up image paramater
6570 pass_along[5] = wp->image;
6572 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6573 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6574 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6575 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6576 gtk_widget_show ( item );
6578 #ifdef VIK_CONFIG_GEOTAG
6579 GtkWidget *geotag_submenu = gtk_menu_new ();
6580 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6581 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6582 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6583 gtk_widget_show ( item );
6584 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6586 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6588 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6589 gtk_widget_show ( item );
6591 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6592 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6593 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6594 gtk_widget_show ( item );
6600 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6601 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6602 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6603 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6604 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6605 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6606 gtk_widget_show ( item );
6613 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6614 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6616 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6617 gtk_widget_show ( item );
6618 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6619 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6620 gtk_widget_set_sensitive ( item, TRUE );
6622 gtk_widget_set_sensitive ( item, FALSE );
6625 item = gtk_menu_item_new ();
6626 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6627 gtk_widget_show ( item );
6630 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6633 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6634 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6635 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6636 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6637 gtk_widget_show ( item );
6640 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6642 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6645 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6646 gtk_widget_show ( item );
6648 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6651 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6652 gtk_widget_show ( item );
6654 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6655 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6657 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6658 gtk_widget_show ( item );
6660 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6661 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6662 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6663 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6664 gtk_widget_show ( item );
6666 GtkWidget *vis_submenu = gtk_menu_new ();
6667 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6668 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6669 gtk_widget_show ( item );
6670 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6672 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
6673 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6674 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
6675 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6676 gtk_widget_show ( item );
6678 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
6679 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6680 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
6681 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6682 gtk_widget_show ( item );
6684 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6685 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6686 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
6687 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6688 gtk_widget_show ( item );
6691 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6695 if ( l->current_track && !l->current_track->is_route ) {
6696 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6697 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6698 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6699 gtk_widget_show ( item );
6701 item = gtk_menu_item_new ();
6702 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6703 gtk_widget_show ( item );
6706 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6707 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6708 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6709 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6710 gtk_widget_show ( item );
6712 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6713 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6714 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6715 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6716 gtk_widget_show ( item );
6717 // Make it available only when a new track *not* already in progress
6718 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6720 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6721 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6722 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6724 gtk_widget_show ( item );
6726 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6727 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6729 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6730 gtk_widget_show ( item );
6732 GtkWidget *vis_submenu = gtk_menu_new ();
6733 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6734 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6735 gtk_widget_show ( item );
6736 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6738 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
6739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
6741 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6742 gtk_widget_show ( item );
6744 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
6745 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6746 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
6747 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6748 gtk_widget_show ( item );
6750 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6751 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
6753 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6754 gtk_widget_show ( item );
6756 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
6758 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6759 gtk_widget_show ( item );
6762 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6766 if ( l->current_track && l->current_track->is_route ) {
6767 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6768 // Reuse finish track method
6769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6770 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6771 gtk_widget_show ( item );
6773 item = gtk_menu_item_new ();
6774 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6775 gtk_widget_show ( item );
6778 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6779 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6781 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6782 gtk_widget_show ( item );
6784 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6785 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6786 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6787 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6788 gtk_widget_show ( item );
6789 // Make it available only when a new track *not* already in progress
6790 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6792 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6793 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6794 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6795 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6796 gtk_widget_show ( item );
6798 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6799 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6800 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6801 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6802 gtk_widget_show ( item );
6804 GtkWidget *vis_submenu = gtk_menu_new ();
6805 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6806 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6807 gtk_widget_show ( item );
6808 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6810 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
6811 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6812 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
6813 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6814 gtk_widget_show ( item );
6816 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
6817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
6819 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6820 gtk_widget_show ( item );
6822 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
6825 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6826 gtk_widget_show ( item );
6828 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
6830 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6831 gtk_widget_show ( item );
6835 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6836 GtkWidget *submenu_sort = gtk_menu_new ();
6837 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
6838 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6839 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6840 gtk_widget_show ( item );
6841 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
6843 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
6844 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
6845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
6846 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
6847 gtk_widget_show ( item );
6849 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
6850 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
6851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
6852 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
6853 gtk_widget_show ( item );
6856 GtkWidget *upload_submenu = gtk_menu_new ();
6858 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6860 item = gtk_menu_item_new ();
6861 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6862 gtk_widget_show ( item );
6864 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6865 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6866 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6867 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6868 if ( l->current_track ) {
6869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6870 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6871 gtk_widget_show ( item );
6874 item = gtk_menu_item_new ();
6875 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6876 gtk_widget_show ( item );
6879 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6880 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6882 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6883 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6884 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6885 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6886 gtk_widget_show ( item );
6888 GtkWidget *goto_submenu;
6889 goto_submenu = gtk_menu_new ();
6890 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6891 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6892 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6893 gtk_widget_show ( item );
6894 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6896 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6897 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6899 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6900 gtk_widget_show ( item );
6902 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6903 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6905 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6906 gtk_widget_show ( item );
6908 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6909 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6911 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6912 gtk_widget_show ( item );
6914 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6915 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6916 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6917 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6918 gtk_widget_show ( item );
6920 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6921 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6922 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6923 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6924 gtk_widget_show ( item );
6926 // Routes don't have speeds
6927 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6928 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6929 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6930 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6931 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6932 gtk_widget_show ( item );
6935 GtkWidget *combine_submenu;
6936 combine_submenu = gtk_menu_new ();
6937 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6939 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6940 gtk_widget_show ( item );
6941 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6943 // Routes don't have times or segments...
6944 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6945 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6947 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6948 gtk_widget_show ( item );
6950 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6952 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6953 gtk_widget_show ( item );
6956 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6958 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6959 gtk_widget_show ( item );
6961 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6962 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6964 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6966 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6967 gtk_widget_show ( item );
6969 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6970 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6972 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6973 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6974 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6975 gtk_widget_show ( item );
6977 GtkWidget *split_submenu;
6978 split_submenu = gtk_menu_new ();
6979 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6980 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6981 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6982 gtk_widget_show ( item );
6983 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6985 // Routes don't have times or segments...
6986 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6987 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6989 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6990 gtk_widget_show ( item );
6992 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6993 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6995 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6996 gtk_widget_show ( item );
6999 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7001 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7002 gtk_widget_show ( item );
7004 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7006 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7007 gtk_widget_show ( item );
7008 // Make it available only when a trackpoint is selected.
7009 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7011 GtkWidget *delete_submenu;
7012 delete_submenu = gtk_menu_new ();
7013 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7014 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7015 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7016 gtk_widget_show ( item );
7017 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7019 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7021 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7022 gtk_widget_show ( item );
7024 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7026 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7027 gtk_widget_show ( item );
7029 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7030 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7032 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7035 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7036 gtk_widget_show ( item );
7038 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7040 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7041 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7043 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7044 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7046 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7047 gtk_widget_show ( item );
7050 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7051 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("DEM Download/Import", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7052 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
7053 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7054 gtk_widget_show ( item );
7056 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7057 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7061 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7062 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7063 gtk_widget_show ( item );
7065 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7066 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7068 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7069 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7071 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7072 gtk_widget_show ( item );
7074 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7075 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7077 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7078 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7079 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7080 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7081 gtk_widget_show ( item );
7083 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7084 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7087 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7088 gtk_widget_show ( item );
7091 // ATM can't upload a single waypoint but can do waypoints to a GPS
7092 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7093 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7095 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7096 gtk_widget_show ( item );
7097 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7099 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7101 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7102 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7103 gtk_widget_show ( item );
7107 #ifdef VIK_CONFIG_GOOGLE
7108 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7110 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7111 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7112 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7113 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7114 gtk_widget_show ( item );
7118 // Some things aren't usable with routes
7119 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7120 #ifdef VIK_CONFIG_OPENSTREETMAP
7121 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7122 // Convert internal pointer into actual track for usage outside this file
7123 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7124 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7126 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7127 gtk_widget_show ( item );
7130 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7133 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7134 gtk_widget_show ( item );
7136 /* ATM This function is only available via the layers panel, due to needing a vlp */
7138 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7139 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7140 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7142 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7143 gtk_widget_show ( item );
7147 #ifdef VIK_CONFIG_GEOTAG
7148 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7149 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7151 gtk_widget_show ( item );
7155 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7156 // Only show on viewport popmenu when a trackpoint is selected
7157 if ( ! vlp && l->current_tpl ) {
7159 item = gtk_menu_item_new ();
7160 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7161 gtk_widget_show ( item );
7163 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7164 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7165 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7166 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7167 gtk_widget_show ( item );
7174 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7177 if (!vtl->current_tpl)
7179 if (!vtl->current_tpl->next)
7182 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7183 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7185 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7188 VikTrackpoint *tp_new = vik_trackpoint_new();
7189 struct LatLon ll_current, ll_next;
7190 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7191 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7193 /* main positional interpolation */
7194 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7195 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7197 /* Now other properties that can be interpolated */
7198 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7200 if (tp_current->has_timestamp && tp_next->has_timestamp) {
7201 /* Note here the division is applied to each part, then added
7202 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7203 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7204 tp_new->has_timestamp = TRUE;
7207 if (tp_current->speed != NAN && tp_next->speed != NAN)
7208 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7210 /* TODO - improve interpolation of course, as it may not be correct.
7211 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7212 [similar applies if value is in radians] */
7213 if (tp_current->course != NAN && tp_next->course != NAN)
7214 tp_new->course = (tp_current->course + tp_next->course)/2;
7216 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7218 /* Insert new point into the trackpoints list after the current TP */
7219 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7221 // Otherwise try routes
7222 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7226 gint index = g_list_index ( trk->trackpoints, tp_current );
7228 // NB no recalculation of bounds since it is inserted between points
7229 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7234 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7240 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7244 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7246 if ( vtl->current_tpl )
7248 vtl->current_tpl = NULL;
7249 vtl->current_tp_track = NULL;
7250 vtl->current_tp_id = NULL;
7251 vik_layer_emit_update(VIK_LAYER(vtl));
7255 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7257 g_assert ( vtl->tpwin != NULL );
7258 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7259 trw_layer_cancel_current_tp ( vtl, TRUE );
7261 if ( vtl->current_tpl == NULL )
7264 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7266 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7267 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7269 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7271 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7273 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7279 // Find available adjacent trackpoint
7280 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7282 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7283 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7285 // Delete current trackpoint
7286 vik_trackpoint_free ( vtl->current_tpl->data );
7287 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7289 // Set to current to the available adjacent trackpoint
7290 vtl->current_tpl = new_tpl;
7292 // Reset dialog with the available adjacent trackpoint
7293 if ( vtl->current_tp_track ) {
7294 vik_track_calculate_bounds ( vtl->current_tp_track );
7295 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7298 vik_layer_emit_update(VIK_LAYER(vtl));
7302 // Delete current trackpoint
7303 vik_trackpoint_free ( vtl->current_tpl->data );
7304 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7305 trw_layer_cancel_current_tp ( vtl, FALSE );
7308 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7310 if ( vtl->current_tp_track )
7311 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7312 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7314 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7316 if ( vtl->current_tp_track )
7317 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7318 vik_layer_emit_update(VIK_LAYER(vtl));
7320 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7322 trw_layer_insert_tp_after_current_tp ( vtl );
7323 vik_layer_emit_update(VIK_LAYER(vtl));
7325 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7326 vik_layer_emit_update(VIK_LAYER(vtl));
7330 * trw_layer_dialog_shift:
7331 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7333 * Try to reposition a dialog if it's over the specified coord
7334 * so to not obscure the item of interest
7336 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7338 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
7340 // Attempt force dialog to be shown so we can find out where it is more reliably...
7341 while ( gtk_events_pending() )
7342 gtk_main_iteration ();
7344 // get parent window position & size
7345 gint win_pos_x, win_pos_y;
7346 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
7348 gint win_size_x, win_size_y;
7349 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
7351 // get own dialog size
7352 gint dia_size_x, dia_size_y;
7353 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
7355 // get own dialog position
7356 gint dia_pos_x, dia_pos_y;
7357 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
7359 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
7360 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
7362 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
7364 gint vp_xx, vp_yy; // In viewport pixels
7365 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
7367 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
7371 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
7373 // Transform Viewport pixels into absolute pixels
7374 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
7375 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
7377 // Is dialog over the point (to within an ^^ edge value)
7378 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
7379 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
7383 gint hh = vik_viewport_get_height ( vvp );
7385 // Consider the difference in viewport to the full window
7386 gint offset_y = dest_y;
7387 // Add difference between dialog and window sizes
7388 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
7390 if ( vp_yy > hh/2 ) {
7391 // Point in bottom half, move window to top half
7392 gtk_window_move ( dialog, dia_pos_x, offset_y );
7395 // Point in top half, move dialog down
7396 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
7400 // Shift left<->right
7401 gint ww = vik_viewport_get_width ( vvp );
7403 // Consider the difference in viewport to the full window
7404 gint offset_x = dest_x;
7405 // Add difference between dialog and window sizes
7406 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
7408 if ( vp_xx > ww/2 ) {
7409 // Point on right, move window to left
7410 gtk_window_move ( dialog, offset_x, dia_pos_y );
7413 // Point on left, move right
7414 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7422 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7426 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7427 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7428 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7429 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7431 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7433 if ( vtl->current_tpl ) {
7434 // get tp pixel position
7435 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7437 // Shift up<->down to try not to obscure the trackpoint.
7438 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7442 if ( vtl->current_tpl )
7443 if ( vtl->current_tp_track )
7444 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7445 /* set layer name and TP data */
7448 /***************************************************************************
7450 ***************************************************************************/
7452 /*** Utility data structures and functions ****/
7456 gint closest_x, closest_y;
7457 gboolean draw_images;
7458 gpointer *closest_wp_id;
7459 VikWaypoint *closest_wp;
7465 gint closest_x, closest_y;
7466 gpointer closest_track_id;
7467 VikTrackpoint *closest_tp;
7473 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7479 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7481 // If waypoint has an image then use the image size to select
7482 if ( params->draw_images && wp->image ) {
7483 gint slackx, slacky;
7484 slackx = wp->image_width / 2;
7485 slacky = wp->image_height / 2;
7487 if ( x <= params->x + slackx && x >= params->x - slackx
7488 && y <= params->y + slacky && y >= params->y - slacky ) {
7489 params->closest_wp_id = id;
7490 params->closest_wp = wp;
7491 params->closest_x = x;
7492 params->closest_y = y;
7495 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7496 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7497 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7499 params->closest_wp_id = id;
7500 params->closest_wp = wp;
7501 params->closest_x = x;
7502 params->closest_y = y;
7506 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7508 GList *tpl = t->trackpoints;
7514 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7520 tp = VIK_TRACKPOINT(tpl->data);
7522 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7524 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7525 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7526 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7528 params->closest_track_id = id;
7529 params->closest_tp = tp;
7530 params->closest_tpl = tpl;
7531 params->closest_x = x;
7532 params->closest_y = y;
7538 // ATM: Leave this as 'Track' only.
7539 // Not overly bothered about having a snap to route trackpoint capability
7540 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7542 TPSearchParams params;
7546 params.closest_track_id = NULL;
7547 params.closest_tp = NULL;
7548 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7549 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7550 return params.closest_tp;
7553 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7555 WPSearchParams params;
7559 params.draw_images = vtl->drawimages;
7560 params.closest_wp = NULL;
7561 params.closest_wp_id = NULL;
7562 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7563 return params.closest_wp;
7567 // Some forward declarations
7568 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7569 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7570 static void marker_end_move ( tool_ed_t *t );
7573 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7577 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7579 // Here always allow snapping back to the original location
7580 // this is useful when one decides not to move the thing afterall
7581 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7584 if ( event->state & GDK_CONTROL_MASK )
7586 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7588 new_coord = tp->coord;
7592 if ( event->state & GDK_SHIFT_MASK )
7594 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7596 new_coord = wp->coord;
7600 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7602 marker_moveto ( t, x, y );
7609 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7611 if ( t->holding && event->button == 1 )
7614 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7617 if ( event->state & GDK_CONTROL_MASK )
7619 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7621 new_coord = tp->coord;
7625 if ( event->state & GDK_SHIFT_MASK )
7627 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7629 new_coord = wp->coord;
7632 marker_end_move ( t );
7634 // Determine if working on a waypoint or a trackpoint
7635 if ( t->is_waypoint ) {
7636 vtl->current_wp->coord = new_coord;
7637 trw_layer_calculate_bounds_waypoints ( vtl );
7640 if ( vtl->current_tpl ) {
7641 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7643 if ( vtl->current_tp_track )
7644 vik_track_calculate_bounds ( vtl->current_tp_track );
7647 if ( vtl->current_tp_track )
7648 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7653 vtl->current_wp = NULL;
7654 vtl->current_wp_id = NULL;
7655 trw_layer_cancel_current_tp ( vtl, FALSE );
7657 vik_layer_emit_update ( VIK_LAYER(vtl) );
7664 Returns true if a waypoint or track is found near the requested event position for this particular layer
7665 The item found is automatically selected
7666 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7668 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7670 if ( event->button != 1 )
7673 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7676 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7680 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7682 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7684 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7685 WPSearchParams wp_params;
7686 wp_params.vvp = vvp;
7687 wp_params.x = event->x;
7688 wp_params.y = event->y;
7689 wp_params.draw_images = vtl->drawimages;
7690 wp_params.closest_wp_id = NULL;
7691 wp_params.closest_wp = NULL;
7693 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7695 if ( wp_params.closest_wp ) {
7698 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7700 // Too easy to move it so must be holding shift to start immediately moving it
7701 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7702 if ( event->state & GDK_SHIFT_MASK ||
7703 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7704 // Put into 'move buffer'
7705 // NB vvp & vw already set in tet
7706 tet->vtl = (gpointer)vtl;
7707 tet->is_waypoint = TRUE;
7709 marker_begin_move (tet, event->x, event->y);
7712 vtl->current_wp = wp_params.closest_wp;
7713 vtl->current_wp_id = wp_params.closest_wp_id;
7715 vik_layer_emit_update ( VIK_LAYER(vtl) );
7721 // Used for both track and route lists
7722 TPSearchParams tp_params;
7723 tp_params.vvp = vvp;
7724 tp_params.x = event->x;
7725 tp_params.y = event->y;
7726 tp_params.closest_track_id = NULL;
7727 tp_params.closest_tp = NULL;
7728 tp_params.closest_tpl = NULL;
7729 tp_params.bbox = bbox;
7731 if (vtl->tracks_visible) {
7732 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7734 if ( tp_params.closest_tp ) {
7736 // Always select + highlight the track
7737 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7739 tet->is_waypoint = FALSE;
7741 // Select the Trackpoint
7742 // Can move it immediately when control held or it's the previously selected tp
7743 if ( event->state & GDK_CONTROL_MASK ||
7744 vtl->current_tpl == tp_params.closest_tpl ) {
7745 // Put into 'move buffer'
7746 // NB vvp & vw already set in tet
7747 tet->vtl = (gpointer)vtl;
7748 marker_begin_move (tet, event->x, event->y);
7751 vtl->current_tpl = tp_params.closest_tpl;
7752 vtl->current_tp_id = tp_params.closest_track_id;
7753 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7755 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7758 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7760 vik_layer_emit_update ( VIK_LAYER(vtl) );
7765 // Try again for routes
7766 if (vtl->routes_visible) {
7767 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7769 if ( tp_params.closest_tp ) {
7771 // Always select + highlight the track
7772 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7774 tet->is_waypoint = FALSE;
7776 // Select the Trackpoint
7777 // Can move it immediately when control held or it's the previously selected tp
7778 if ( event->state & GDK_CONTROL_MASK ||
7779 vtl->current_tpl == tp_params.closest_tpl ) {
7780 // Put into 'move buffer'
7781 // NB vvp & vw already set in tet
7782 tet->vtl = (gpointer)vtl;
7783 marker_begin_move (tet, event->x, event->y);
7786 vtl->current_tpl = tp_params.closest_tpl;
7787 vtl->current_tp_id = tp_params.closest_track_id;
7788 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7790 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7793 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7795 vik_layer_emit_update ( VIK_LAYER(vtl) );
7800 /* these aren't the droids you're looking for */
7801 vtl->current_wp = NULL;
7802 vtl->current_wp_id = NULL;
7803 trw_layer_cancel_current_tp ( vtl, FALSE );
7806 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7811 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7813 if ( event->button != 3 )
7816 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7819 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7822 /* Post menu for the currently selected item */
7824 /* See if a track is selected */
7825 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7826 if ( track && track->visible ) {
7828 if ( track->name ) {
7830 if ( vtl->track_right_click_menu )
7831 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7833 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7840 if ( track->is_route )
7841 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7843 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7845 if ( trkf && udataU.uuid ) {
7848 if ( track->is_route )
7849 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7851 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7853 trw_layer_sublayer_add_menu_items ( vtl,
7854 vtl->track_right_click_menu,
7856 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7862 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7868 /* See if a waypoint is selected */
7869 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7870 if ( waypoint && waypoint->visible ) {
7871 if ( waypoint->name ) {
7873 if ( vtl->wp_right_click_menu )
7874 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7876 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7879 udata.wp = waypoint;
7882 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7884 if ( wpf && udata.uuid ) {
7885 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7887 trw_layer_sublayer_add_menu_items ( vtl,
7888 vtl->wp_right_click_menu,
7890 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7895 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7904 /* background drawing hook, to be passed the viewport */
7905 static gboolean tool_sync_done = TRUE;
7907 static gboolean tool_sync(gpointer data)
7909 VikViewport *vvp = data;
7910 gdk_threads_enter();
7911 vik_viewport_sync(vvp);
7912 tool_sync_done = TRUE;
7913 gdk_threads_leave();
7917 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7920 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7921 gdk_gc_set_function ( t->gc, GDK_INVERT );
7922 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7923 vik_viewport_sync(t->vvp);
7928 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7930 VikViewport *vvp = t->vvp;
7931 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7932 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7936 if (tool_sync_done) {
7937 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7938 tool_sync_done = FALSE;
7942 static void marker_end_move ( tool_ed_t *t )
7944 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7945 g_object_unref ( t->gc );
7949 /*** Edit waypoint ****/
7951 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7953 tool_ed_t *t = g_new(tool_ed_t, 1);
7959 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
7964 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7966 WPSearchParams params;
7967 tool_ed_t *t = data;
7968 VikViewport *vvp = t->vvp;
7970 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7977 if ( !vtl->vl.visible || !vtl->waypoints_visible )
7980 if ( vtl->current_wp && vtl->current_wp->visible )
7982 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7984 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7986 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7987 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7989 if ( event->button == 3 )
7990 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7992 marker_begin_move(t, event->x, event->y);
7999 params.x = event->x;
8000 params.y = event->y;
8001 params.draw_images = vtl->drawimages;
8002 params.closest_wp_id = NULL;
8003 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8004 params.closest_wp = NULL;
8005 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8006 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8008 // how do we get here?
8009 marker_begin_move(t, event->x, event->y);
8010 g_critical("shouldn't be here");
8013 else if ( params.closest_wp )
8015 if ( event->button == 3 )
8016 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8018 vtl->waypoint_rightclick = FALSE;
8020 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8022 vtl->current_wp = params.closest_wp;
8023 vtl->current_wp_id = params.closest_wp_id;
8025 /* could make it so don't update if old WP is off screen and new is null but oh well */
8026 vik_layer_emit_update ( VIK_LAYER(vtl) );
8030 vtl->current_wp = NULL;
8031 vtl->current_wp_id = NULL;
8032 vtl->waypoint_rightclick = FALSE;
8033 vik_layer_emit_update ( VIK_LAYER(vtl) );
8037 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8039 tool_ed_t *t = data;
8040 VikViewport *vvp = t->vvp;
8042 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8047 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8050 if ( event->state & GDK_CONTROL_MASK )
8052 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8054 new_coord = tp->coord;
8058 if ( event->state & GDK_SHIFT_MASK )
8060 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8061 if ( wp && wp != vtl->current_wp )
8062 new_coord = wp->coord;
8067 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8069 marker_moveto ( t, x, y );
8076 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8078 tool_ed_t *t = data;
8079 VikViewport *vvp = t->vvp;
8081 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8084 if ( t->holding && event->button == 1 )
8087 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8090 if ( event->state & GDK_CONTROL_MASK )
8092 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8094 new_coord = tp->coord;
8098 if ( event->state & GDK_SHIFT_MASK )
8100 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8101 if ( wp && wp != vtl->current_wp )
8102 new_coord = wp->coord;
8105 marker_end_move ( t );
8107 vtl->current_wp->coord = new_coord;
8109 trw_layer_calculate_bounds_waypoints ( vtl );
8110 vik_layer_emit_update ( VIK_LAYER(vtl) );
8113 /* PUT IN RIGHT PLACE!!! */
8114 if ( event->button == 3 && vtl->waypoint_rightclick )
8116 if ( vtl->wp_right_click_menu )
8117 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8118 if ( vtl->current_wp ) {
8119 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8120 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 );
8121 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8123 vtl->waypoint_rightclick = FALSE;
8128 /*** New track ****/
8130 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8137 GdkDrawable *drawable;
8143 * Draw specified pixmap
8145 static gboolean draw_sync ( gpointer data )
8147 draw_sync_t *ds = (draw_sync_t*) data;
8148 // Sometimes don't want to draw
8149 // normally because another update has taken precedent such as panning the display
8150 // which means this pixmap is no longer valid
8151 if ( ds->vtl->draw_sync_do ) {
8152 gdk_threads_enter();
8153 gdk_draw_drawable (ds->drawable,
8156 0, 0, 0, 0, -1, -1);
8157 ds->vtl->draw_sync_done = TRUE;
8158 gdk_threads_leave();
8163 static gchar* distance_string (gdouble distance)
8167 /* draw label with distance */
8168 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8169 switch (dist_units) {
8170 case VIK_UNITS_DISTANCE_MILES:
8171 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8172 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8173 } else if (distance < 1609.4) {
8174 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8176 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8180 // VIK_UNITS_DISTANCE_KILOMETRES
8181 if (distance >= 1000 && distance < 100000) {
8182 g_sprintf(str, "%3.2f km", distance/1000.0);
8183 } else if (distance < 1000) {
8184 g_sprintf(str, "%d m", (int)distance);
8186 g_sprintf(str, "%d km", (int)distance/1000);
8190 return g_strdup (str);
8194 * Actually set the message in statusbar
8196 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8198 // Only show elevation data when track has some elevation properties
8199 gchar str_gain_loss[64];
8200 str_gain_loss[0] = '\0';
8201 gchar str_last_step[64];
8202 str_last_step[0] = '\0';
8203 gchar *str_total = distance_string (distance);
8205 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8206 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8207 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8209 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8212 if ( last_step > 0 ) {
8213 gchar *tmp = distance_string (last_step);
8214 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8218 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8220 // Write with full gain/loss information
8221 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8222 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8224 g_free ( str_total );
8228 * Figure out what information should be set in the statusbar and then write it
8230 static void update_statusbar ( VikTrwLayer *vtl )
8232 // Get elevation data
8233 gdouble elev_gain, elev_loss;
8234 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8236 /* Find out actual distance of current track */
8237 gdouble distance = vik_track_get_length (vtl->current_track);
8239 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8243 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8245 /* if we haven't sync'ed yet, we don't have time to do more. */
8246 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8247 GList *iter = g_list_last ( vtl->current_track->trackpoints );
8248 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8250 static GdkPixmap *pixmap = NULL;
8252 // Need to check in case window has been resized
8253 w1 = vik_viewport_get_width(vvp);
8254 h1 = vik_viewport_get_height(vvp);
8256 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8258 gdk_drawable_get_size (pixmap, &w2, &h2);
8259 if (w1 != w2 || h1 != h2) {
8260 g_object_unref ( G_OBJECT ( pixmap ) );
8261 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8264 // Reset to background
8265 gdk_draw_drawable (pixmap,
8266 vtl->current_track_newpoint_gc,
8267 vik_viewport_get_pixmap(vvp),
8268 0, 0, 0, 0, -1, -1);
8270 draw_sync_t *passalong;
8273 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8275 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8276 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8277 // thus when we come to reset to the background it would include what we have already drawn!!
8278 gdk_draw_line ( pixmap,
8279 vtl->current_track_newpoint_gc,
8280 x1, y1, event->x, event->y );
8281 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8283 /* Find out actual distance of current track */
8284 gdouble distance = vik_track_get_length (vtl->current_track);
8286 // Now add distance to where the pointer is //
8289 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8290 vik_coord_to_latlon ( &coord, &ll );
8291 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8292 distance = distance + last_step;
8294 // Get elevation data
8295 gdouble elev_gain, elev_loss;
8296 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8298 // Adjust elevation data (if available) for the current pointer position
8300 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8301 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8302 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8303 // Adjust elevation of last track point
8304 if ( elev_new > last_tpt->altitude )
8306 elev_gain += elev_new - last_tpt->altitude;
8309 elev_loss += last_tpt->altitude - elev_new;
8314 // Display of the distance 'tooltip' during track creation is controlled by a preference
8316 if ( a_vik_get_create_track_tooltip() ) {
8318 gchar *str = distance_string (distance);
8320 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8321 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8322 pango_layout_set_text (pl, str, -1);
8324 pango_layout_get_pixel_size ( pl, &wd, &hd );
8327 // offset from cursor a bit depending on font size
8331 // Create a background block to make the text easier to read over the background map
8332 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8333 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8334 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8336 g_object_unref ( G_OBJECT ( pl ) );
8337 g_object_unref ( G_OBJECT ( background_block_gc ) );
8341 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
8342 passalong->vtl = vtl;
8343 passalong->pixmap = pixmap;
8344 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
8345 passalong->gc = vtl->current_track_newpoint_gc;
8349 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
8351 // Update statusbar with full gain/loss information
8352 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
8354 // draw pixmap when we have time to
8355 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
8356 vtl->draw_sync_done = FALSE;
8357 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
8359 return VIK_LAYER_TOOL_ACK;
8362 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
8364 if ( vtl->current_track && event->keyval == GDK_Escape ) {
8365 vtl->current_track = NULL;
8366 vik_layer_emit_update ( VIK_LAYER(vtl) );
8368 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
8370 if ( vtl->current_track->trackpoints )
8372 GList *last = g_list_last(vtl->current_track->trackpoints);
8373 g_free ( last->data );
8374 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8377 update_statusbar ( vtl );
8379 vik_layer_emit_update ( VIK_LAYER(vtl) );
8386 * Common function to handle trackpoint button requests on either a route or a track
8387 * . enables adding a point via normal click
8388 * . enables removal of last point via right click
8389 * . finishing of the track or route via double clicking
8391 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8395 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8398 if ( event->button == 2 ) {
8399 // As the display is panning, the new track pixmap is now invalid so don't draw it
8400 // otherwise this drawing done results in flickering back to an old image
8401 vtl->draw_sync_do = FALSE;
8405 if ( event->button == 3 )
8407 if ( !vtl->current_track )
8410 if ( vtl->current_track->trackpoints )
8412 GList *last = g_list_last(vtl->current_track->trackpoints);
8413 g_free ( last->data );
8414 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8416 vik_track_calculate_bounds ( vtl->current_track );
8417 update_statusbar ( vtl );
8419 vik_layer_emit_update ( VIK_LAYER(vtl) );
8423 if ( event->type == GDK_2BUTTON_PRESS )
8425 /* subtract last (duplicate from double click) tp then end */
8426 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8428 GList *last = g_list_last(vtl->current_track->trackpoints);
8429 g_free ( last->data );
8430 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8431 /* undo last, then end */
8432 vtl->current_track = NULL;
8434 vik_layer_emit_update ( VIK_LAYER(vtl) );
8438 tp = vik_trackpoint_new();
8439 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8441 /* snap to other TP */
8442 if ( event->state & GDK_CONTROL_MASK )
8444 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8446 tp->coord = other_tp->coord;
8449 tp->newsegment = FALSE;
8450 tp->has_timestamp = FALSE;
8453 if ( vtl->current_track ) {
8454 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
8455 /* Auto attempt to get elevation from DEM data (if it's available) */
8456 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8459 vtl->ct_x1 = vtl->ct_x2;
8460 vtl->ct_y1 = vtl->ct_y2;
8461 vtl->ct_x2 = event->x;
8462 vtl->ct_y2 = event->y;
8464 vik_layer_emit_update ( VIK_LAYER(vtl) );
8468 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8470 // ----------------------------------------------------- if current is a route - switch to new track
8471 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8473 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8474 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8476 new_track_create_common ( vtl, name );
8482 return tool_new_track_or_route_click ( vtl, event, vvp );
8485 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8487 if ( event->button == 2 ) {
8488 // Pan moving ended - enable potential point drawing again
8489 vtl->draw_sync_do = TRUE;
8490 vtl->draw_sync_done = TRUE;
8494 /*** New route ****/
8496 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8501 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8503 // -------------------------- if current is a track - switch to new route
8504 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8506 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8507 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) ) {
8508 new_route_create_common ( vtl, name );
8514 return tool_new_track_or_route_click ( vtl, event, vvp );
8517 /*** New waypoint ****/
8519 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8524 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8527 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8529 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8530 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8531 trw_layer_calculate_bounds_waypoints ( vtl );
8532 vik_layer_emit_update ( VIK_LAYER(vtl) );
8538 /*** Edit trackpoint ****/
8540 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8542 tool_ed_t *t = g_new(tool_ed_t, 1);
8548 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
8553 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8555 tool_ed_t *t = data;
8556 VikViewport *vvp = t->vvp;
8557 TPSearchParams params;
8558 /* OUTDATED DOCUMENTATION:
8559 find 5 pixel range on each side. then put these UTM, and a pointer
8560 to the winning track name (and maybe the winning track itself), and a
8561 pointer to the winning trackpoint, inside an array or struct. pass
8562 this along, do a foreach on the tracks which will do a foreach on the
8565 params.x = event->x;
8566 params.y = event->y;
8567 params.closest_track_id = NULL;
8568 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8569 params.closest_tp = NULL;
8570 params.closest_tpl = NULL;
8571 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8573 if ( event->button != 1 )
8576 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8579 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8582 if ( vtl->current_tpl )
8584 /* 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.) */
8585 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8586 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8591 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8593 if ( current_tr->visible &&
8594 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8595 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8596 marker_begin_move ( t, event->x, event->y );
8602 if ( vtl->tracks_visible )
8603 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8605 if ( params.closest_tp )
8607 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8608 vtl->current_tpl = params.closest_tpl;
8609 vtl->current_tp_id = params.closest_track_id;
8610 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8611 trw_layer_tpwin_init ( vtl );
8612 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8613 vik_layer_emit_update ( VIK_LAYER(vtl) );
8617 if ( vtl->routes_visible )
8618 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8620 if ( params.closest_tp )
8622 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8623 vtl->current_tpl = params.closest_tpl;
8624 vtl->current_tp_id = params.closest_track_id;
8625 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8626 trw_layer_tpwin_init ( vtl );
8627 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8628 vik_layer_emit_update ( VIK_LAYER(vtl) );
8632 /* these aren't the droids you're looking for */
8636 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8638 tool_ed_t *t = data;
8639 VikViewport *vvp = t->vvp;
8641 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8647 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8650 if ( event->state & GDK_CONTROL_MASK )
8652 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8653 if ( tp && tp != vtl->current_tpl->data )
8654 new_coord = tp->coord;
8656 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8659 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8660 marker_moveto ( t, x, y );
8668 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8670 tool_ed_t *t = data;
8671 VikViewport *vvp = t->vvp;
8673 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8675 if ( event->button != 1)
8680 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8683 if ( event->state & GDK_CONTROL_MASK )
8685 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8686 if ( tp && tp != vtl->current_tpl->data )
8687 new_coord = tp->coord;
8690 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8691 if ( vtl->current_tp_track )
8692 vik_track_calculate_bounds ( vtl->current_tp_track );
8694 marker_end_move ( t );
8696 /* diff dist is diff from orig */
8698 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8700 vik_layer_emit_update ( VIK_LAYER(vtl) );
8707 /*** Route Finder ***/
8708 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8713 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8716 if ( !vtl ) return FALSE;
8717 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8718 if ( event->button == 3 && vtl->route_finder_current_track ) {
8720 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8722 vtl->route_finder_coord = *new_end;
8724 vik_layer_emit_update ( VIK_LAYER(vtl) );
8725 /* remove last ' to:...' */
8726 if ( vtl->route_finder_current_track->comment ) {
8727 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8728 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8729 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8730 last_to - vtl->route_finder_current_track->comment - 1);
8731 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8736 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8737 struct LatLon start, end;
8739 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8740 vik_coord_to_latlon ( &(tmp), &end );
8741 vtl->route_finder_coord = tmp; /* for continuations */
8743 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8744 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8745 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8747 vtl->route_finder_check_added_track = TRUE;
8748 vtl->route_finder_started = FALSE;
8751 vik_routing_default_find ( vtl, start, end);
8753 /* see if anything was done -- a track was added or appended to */
8754 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8755 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 ) );
8756 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8757 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8758 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8759 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8762 if ( vtl->route_finder_added_track )
8763 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8765 vtl->route_finder_added_track = NULL;
8766 vtl->route_finder_check_added_track = FALSE;
8767 vtl->route_finder_append = FALSE;
8769 vik_layer_emit_update ( VIK_LAYER(vtl) );
8771 vtl->route_finder_started = TRUE;
8772 vtl->route_finder_coord = tmp;
8773 vtl->route_finder_current_track = NULL;
8778 /*** Show picture ****/
8780 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8785 /* Params are: vvp, event, last match found or NULL */
8786 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8788 if ( wp->image && wp->visible )
8790 gint x, y, slackx, slacky;
8791 GdkEventButton *event = (GdkEventButton *) params[1];
8793 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8794 slackx = wp->image_width / 2;
8795 slacky = wp->image_height / 2;
8796 if ( x <= event->x + slackx && x >= event->x - slackx
8797 && y <= event->y + slacky && y >= event->y - slacky )
8799 params[2] = wp->image; /* we've found a match. however continue searching
8800 * since we want to find the last match -- that
8801 * is, the match that was drawn last. */
8806 static void trw_layer_show_picture ( gpointer pass_along[6] )
8808 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8810 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8813 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8814 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8815 g_free ( quoted_file );
8816 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8818 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() );
8819 g_error_free ( err );
8822 #endif /* WINDOWS */
8825 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8827 gpointer params[3] = { vvp, event, NULL };
8828 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8830 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8833 static gpointer pass_along[6];
8834 pass_along[0] = vtl;
8835 pass_along[5] = params[2];
8836 trw_layer_show_picture ( pass_along );
8837 return TRUE; /* found a match */
8840 return FALSE; /* go through other layers, searching for a match */
8843 /***************************************************************************
8845 ***************************************************************************/
8848 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8850 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8851 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8854 /* Structure for thumbnail creating data used in the background thread */
8856 VikTrwLayer *vtl; // Layer needed for redrawing
8857 GSList *pics; // Image list
8858 } thumbnail_create_thread_data;
8860 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8862 guint total = g_slist_length(tctd->pics), done = 0;
8863 while ( tctd->pics )
8865 a_thumbnails_create ( (gchar *) tctd->pics->data );
8866 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8868 return -1; /* Abort thread */
8870 tctd->pics = tctd->pics->next;
8873 // Redraw to show the thumbnails as they are now created
8874 if ( IS_VIK_LAYER(tctd->vtl) )
8875 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8880 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8882 while ( tctd->pics )
8884 g_free ( tctd->pics->data );
8885 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8890 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8892 if ( ! vtl->has_verified_thumbnails )
8894 GSList *pics = NULL;
8895 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8898 gint len = g_slist_length ( pics );
8899 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8900 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8903 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8905 (vik_thr_func) create_thumbnails_thread,
8907 (vik_thr_free_func) thumbnail_create_thread_free,
8915 static const gchar* my_track_colors ( gint ii )
8917 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8929 // Fast and reliable way of returning a colour
8930 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8933 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8935 GHashTableIter iter;
8936 gpointer key, value;
8940 g_hash_table_iter_init ( &iter, vtl->tracks );
8942 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8944 // Tracks get a random spread of colours if not already assigned
8945 if ( ! VIK_TRACK(value)->has_color ) {
8946 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8947 VIK_TRACK(value)->color = vtl->track_color;
8949 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8951 VIK_TRACK(value)->has_color = TRUE;
8954 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8957 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8963 g_hash_table_iter_init ( &iter, vtl->routes );
8965 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8967 // Routes get an intermix of reds
8968 if ( ! VIK_TRACK(value)->has_color ) {
8970 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8972 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8973 VIK_TRACK(value)->has_color = TRUE;
8976 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8983 * (Re)Calculate the bounds of the waypoints in this layer,
8984 * This should be called whenever waypoints are changed
8986 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8988 struct LatLon topleft = { 0.0, 0.0 };
8989 struct LatLon bottomright = { 0.0, 0.0 };
8992 GHashTableIter iter;
8993 gpointer key, value;
8995 g_hash_table_iter_init ( &iter, vtl->waypoints );
8997 // Set bounds to first point
8998 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
8999 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9000 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9003 // Ensure there is another point...
9004 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9006 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9008 // See if this point increases the bounds.
9009 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9011 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9012 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9013 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9014 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9018 vtl->waypoints_bbox.north = topleft.lat;
9019 vtl->waypoints_bbox.east = bottomright.lon;
9020 vtl->waypoints_bbox.south = bottomright.lat;
9021 vtl->waypoints_bbox.west = topleft.lon;
9024 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9026 vik_track_calculate_bounds ( trk );
9029 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9031 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9032 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9035 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9037 if ( ! VIK_LAYER(vtl)->vt )
9040 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9041 if ( g_hash_table_size (vtl->tracks) > 1 )
9042 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9044 if ( g_hash_table_size (vtl->routes) > 1 )
9045 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9047 if ( g_hash_table_size (vtl->waypoints) > 1 )
9048 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9051 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9053 trw_layer_verify_thumbnails ( vtl, vvp );
9054 trw_layer_track_alloc_colors ( vtl );
9056 trw_layer_calculate_bounds_waypoints ( vtl );
9057 trw_layer_calculate_bounds_tracks ( vtl );
9059 // Apply treeview sort after loading all the tracks for this layer
9060 // (rather than sorted insert on each individual track additional)
9061 // and after subsequent changes to the properties as the specified order may have changed.
9062 // since the sorting of a treeview section is now very quick
9063 // NB sorting is also performed after every name change as well to maintain the list order
9064 trw_layer_sort_all ( vtl );
9067 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9069 return vtl->coord_mode;
9073 * Uniquify the whole layer
9074 * Also requires the layers panel as the names shown there need updating too
9075 * Returns whether the operation was successful or not
9077 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9080 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9081 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9082 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9088 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9090 vik_coord_convert ( &(wp->coord), *dest_mode );
9093 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9095 vik_track_convert ( tr, *dest_mode );
9098 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9100 if ( vtl->coord_mode != dest_mode )
9102 vtl->coord_mode = dest_mode;
9103 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9104 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9105 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9109 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9111 vtl->menu_selection = selection;
9114 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9116 return (vtl->menu_selection);
9119 /* ----------- Downloading maps along tracks --------------- */
9121 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9123 /* TODO: calculating based on current size of viewport */
9124 const gdouble w_at_zoom_0_125 = 0.0013;
9125 const gdouble h_at_zoom_0_125 = 0.0011;
9126 gdouble zoom_factor = zoom_level/0.125;
9128 wh->lat = h_at_zoom_0_125 * zoom_factor;
9129 wh->lon = w_at_zoom_0_125 * zoom_factor;
9131 return 0; /* all OK */
9134 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9136 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9137 (dist->lat >= ABS(to->north_south - from->north_south)))
9140 VikCoord *coord = g_malloc(sizeof(VikCoord));
9141 coord->mode = VIK_COORD_LATLON;
9143 if (ABS(gradient) < 1) {
9144 if (from->east_west > to->east_west)
9145 coord->east_west = from->east_west - dist->lon;
9147 coord->east_west = from->east_west + dist->lon;
9148 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9150 if (from->north_south > to->north_south)
9151 coord->north_south = from->north_south - dist->lat;
9153 coord->north_south = from->north_south + dist->lat;
9154 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9160 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9162 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9163 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9165 VikCoord *next = from;
9167 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9169 list = g_list_prepend(list, next);
9175 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9177 typedef struct _Rect {
9182 #define GLRECT(iter) ((Rect *)((iter)->data))
9185 GList *rects_to_download = NULL;
9188 if (get_download_area_width(vvp, zoom_level, &wh))
9191 GList *iter = tr->trackpoints;
9195 gboolean new_map = TRUE;
9196 VikCoord *cur_coord, tl, br;
9199 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9201 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9202 rect = g_malloc(sizeof(Rect));
9205 rect->center = *cur_coord;
9206 rects_to_download = g_list_prepend(rects_to_download, rect);
9211 gboolean found = FALSE;
9212 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9213 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9224 GList *fillins = NULL;
9225 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9226 /* seems that ATM the function get_next_coord works only for LATLON */
9227 if ( cur_coord->mode == VIK_COORD_LATLON ) {
9228 /* fill-ins for far apart points */
9229 GList *cur_rect, *next_rect;
9230 for (cur_rect = rects_to_download;
9231 (next_rect = cur_rect->next) != NULL;
9232 cur_rect = cur_rect->next) {
9233 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9234 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9235 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9239 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9242 GList *iter = fillins;
9244 cur_coord = (VikCoord *)(iter->data);
9245 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9246 rect = g_malloc(sizeof(Rect));
9249 rect->center = *cur_coord;
9250 rects_to_download = g_list_prepend(rects_to_download, rect);
9255 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9256 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9260 for (iter = fillins; iter; iter = iter->next)
9262 g_list_free(fillins);
9264 if (rects_to_download) {
9265 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9266 g_free(rect_iter->data);
9267 g_list_free(rects_to_download);
9271 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9274 gint selected_map, default_map;
9275 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9276 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9277 gint selected_zoom, default_zoom;
9281 VikTrwLayer *vtl = pass_along[0];
9282 VikLayersPanel *vlp = pass_along[1];
9284 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9285 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9287 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9291 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9293 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9294 int num_maps = g_list_length(vmls);
9297 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
9301 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9302 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9304 gchar **np = map_names;
9305 VikMapsLayer **lp = map_layers;
9306 for (i = 0; i < num_maps; i++) {
9307 gboolean dup = FALSE;
9308 vml = (VikMapsLayer *)(vmls->data);
9309 for (j = 0; j < i; j++) { /* no duplicate allowed */
9310 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
9317 *np++ = vik_maps_layer_get_map_label(vml);
9323 num_maps = lp - map_layers;
9325 for (default_map = 0; default_map < num_maps; default_map++) {
9326 /* TODO: check for parent layer's visibility */
9327 if (VIK_LAYER(map_layers[default_map])->visible)
9330 default_map = (default_map == num_maps) ? 0 : default_map;
9332 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9333 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
9334 if (cur_zoom == zoom_vals[default_zoom])
9337 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
9339 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
9342 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9345 for (i = 0; i < num_maps; i++)
9346 g_free(map_names[i]);
9354 /**** lowest waypoint number calculation ***/
9355 static gint highest_wp_number_name_to_number(const gchar *name) {
9356 if ( strlen(name) == 3 ) {
9358 if ( n < 100 && name[0] != '0' )
9360 if ( n < 10 && name[0] != '0' )
9368 static void highest_wp_number_reset(VikTrwLayer *vtl)
9370 vtl->highest_wp_number = -1;
9373 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
9375 /* if is bigger that top, add it */
9376 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
9377 if ( new_wp_num > vtl->highest_wp_number )
9378 vtl->highest_wp_number = new_wp_num;
9381 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
9383 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
9384 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
9385 if ( vtl->highest_wp_number == old_wp_num ) {
9387 vtl->highest_wp_number--;
9389 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9390 /* search down until we find something that *does* exist */
9392 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
9393 vtl->highest_wp_number--;
9394 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9399 /* get lowest unused number */
9400 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
9403 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
9405 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
9406 return g_strdup(buf);