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);
4503 * distance_in_preferred_units:
4504 * @dist: The source distance in standard SI Units (i.e. metres)
4506 * TODO: This is a generic function that could be moved into globals.c or utils.c
4508 * Probably best used if you have a only few conversions to perform.
4509 * However if doing many points (such as on all points along a track) then this may be a bit slow,
4510 * since it will be doing the preference check on each call
4512 * Returns: The distance in the units as specified by the preferences
4514 static gdouble distance_in_preferred_units ( gdouble dist )
4517 vik_units_distance_t dist_units = a_vik_get_units_distance ();
4518 switch (dist_units) {
4519 case VIK_UNITS_DISTANCE_MILES:
4520 mydist = VIK_METERS_TO_MILES(dist);
4522 // VIK_UNITS_DISTANCE_KILOMETRES:
4524 mydist = dist/1000.0;
4530 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4532 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4534 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4535 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4537 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4542 // Converting a track to a route can be a bit more complicated,
4543 // so give a chance to change our minds:
4544 if ( !trk->is_route &&
4545 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4546 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4548 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4549 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4554 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4557 trk_copy->is_route = !trk_copy->is_route;
4559 // ATM can't set name to self - so must create temporary copy
4560 gchar *name = g_strdup ( trk_copy->name );
4562 // Delete old one and then add new one
4563 if ( trk->is_route ) {
4564 vik_trw_layer_delete_route ( vtl, trk );
4565 vik_trw_layer_add_track ( vtl, name, trk_copy );
4568 // Extra route conversion bits...
4569 vik_track_merge_segments ( trk_copy );
4570 vik_track_to_routepoints ( trk_copy );
4572 vik_trw_layer_delete_track ( vtl, trk );
4573 vik_trw_layer_add_route ( vtl, name, trk_copy );
4577 // Update in case color of track / route changes when moving between sublayers
4578 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4582 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4584 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4586 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4587 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4589 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4594 vtl->current_track = track;
4595 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);
4597 if ( track->trackpoints )
4598 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4602 * extend a track using route finder
4604 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4606 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4607 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4610 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4612 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4613 vtl->route_finder_coord = last_coord;
4614 vtl->route_finder_current_track = track;
4615 vtl->route_finder_started = TRUE;
4617 if ( track->trackpoints )
4618 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4622 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4624 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4625 /* Also warn if overwrite old elevation data */
4626 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4628 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4629 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4631 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4634 vik_track_apply_dem_data ( track );
4637 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4639 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4641 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4642 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4644 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4649 GList *trps = track->trackpoints;
4652 trps = g_list_last(trps);
4653 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4656 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4658 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4660 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4661 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4663 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4668 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4671 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4674 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4676 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4678 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4679 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4681 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4686 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4689 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4692 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4694 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4696 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4697 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4699 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4704 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4707 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4711 * Automatically change the viewport to center on the track and zoom to see the extent of the track
4713 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4715 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4717 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4718 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4720 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4722 if ( trk && trk->trackpoints )
4724 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4725 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4726 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
4727 if ( pass_along[1] )
4728 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4730 vik_layer_emit_update ( VIK_LAYER(vtl) );
4734 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4736 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4737 trw_layer_tpwin_init ( vtl );
4740 /*************************************
4741 * merge/split by time routines
4742 *************************************/
4744 /* called for each key in track hash table.
4745 * If the current track has the same time stamp type, add it to the result,
4746 * except the one pointed by "exclude".
4747 * set exclude to NULL if there is no exclude to check.
4748 * Note that the result is in reverse (for performance reasons).
4753 gboolean with_timestamps;
4755 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4757 twt_udata *user_data = udata;
4758 VikTrackpoint *p1, *p2;
4760 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4764 if (VIK_TRACK(value)->trackpoints) {
4765 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4766 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4768 if ( user_data->with_timestamps ) {
4769 if (!p1->has_timestamp || !p2->has_timestamp) {
4774 // Don't add tracks with timestamps when getting non timestamp tracks
4775 if (p1->has_timestamp || p2->has_timestamp) {
4781 *(user_data->result) = g_list_prepend(*(user_data->result), key);
4784 /* called for each key in track hash table. if original track user_data[1] is close enough
4785 * to the passed one, add it to list in user_data[0]
4787 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4790 VikTrackpoint *p1, *p2;
4791 VikTrack *trk = VIK_TRACK(value);
4793 GList **nearby_tracks = ((gpointer *)user_data)[0];
4794 GList *tpoints = ((gpointer *)user_data)[1];
4797 * detect reasons for not merging, and return
4798 * if no reason is found not to merge, then do it.
4801 // Exclude the original track from the compiled list
4802 if (trk->trackpoints == tpoints) {
4806 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4807 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4809 if (trk->trackpoints) {
4810 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4811 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4813 if (!p1->has_timestamp || !p2->has_timestamp) {
4814 //g_print("no timestamp\n");
4818 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4819 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4820 if (! (abs(t1 - p2->timestamp) < threshold ||
4822 abs(p1->timestamp - t2) < threshold)
4829 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4832 /* comparison function used to sort tracks; a and b are hash table keys */
4833 /* Not actively used - can be restored if needed
4834 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4836 GHashTable *tracks = user_data;
4839 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4840 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4842 if (t1 < t2) return -1;
4843 if (t1 > t2) return 1;
4848 /* comparison function used to sort trackpoints */
4849 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4851 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4853 if (t1 < t2) return -1;
4854 if (t1 > t2) return 1;
4859 * comparison function which can be used to sort tracks or waypoints by name
4861 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4863 const gchar* namea = (const gchar*) a;
4864 const gchar* nameb = (const gchar*) b;
4865 if ( namea == NULL || nameb == NULL)
4868 // Same sort method as used in the vik_treeview_*_alphabetize functions
4869 return strcmp ( namea, nameb );
4873 * Attempt to merge selected track with other tracks specified by the user
4874 * Tracks to merge with must be of the same 'type' as the selected track -
4875 * either all with timestamps, or all without timestamps
4877 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4879 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4880 GList *other_tracks = NULL;
4881 GHashTable *ght_tracks;
4882 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4883 ght_tracks = vtl->routes;
4885 ght_tracks = vtl->tracks;
4887 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4892 if ( !track->trackpoints )
4896 udata.result = &other_tracks;
4897 udata.exclude = track->trackpoints;
4898 // Allow merging with 'similar' time type time tracks
4899 // i.e. either those times, or those without
4900 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4902 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4903 other_tracks = g_list_reverse(other_tracks);
4905 if ( !other_tracks ) {
4906 if ( udata.with_timestamps )
4907 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4909 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4913 // Sort alphabetically for user presentation
4914 // Convert into list of names for usage with dialog function
4915 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4916 GList *other_tracks_names = NULL;
4917 GList *iter = g_list_first ( other_tracks );
4919 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4920 iter = g_list_next ( iter );
4923 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4925 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4929 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4930 g_list_free(other_tracks);
4931 g_list_free(other_tracks_names);
4936 for (l = merge_list; l != NULL; l = g_list_next(l)) {
4937 VikTrack *merge_track;
4938 if ( track->is_route )
4939 merge_track = vik_trw_layer_get_route ( vtl, l->data );
4941 merge_track = vik_trw_layer_get_track ( vtl, l->data );
4944 vik_track_steal_and_append_trackpoints ( track, merge_track );
4945 if ( track->is_route )
4946 vik_trw_layer_delete_route (vtl, merge_track);
4948 vik_trw_layer_delete_track (vtl, merge_track);
4949 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4952 for (l = merge_list; l != NULL; l = g_list_next(l))
4954 g_list_free(merge_list);
4956 vik_layer_emit_update( VIK_LAYER(vtl) );
4960 // c.f. trw_layer_sorted_track_id_by_name_list
4961 // but don't add the specified track to the list (normally current track)
4962 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4964 twt_udata *user_data = udata;
4967 if (trk->trackpoints == user_data->exclude) {
4971 // Sort named list alphabetically
4972 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4976 * Join - this allows combining 'tracks' and 'track routes'
4977 * i.e. doesn't care about whether tracks have consistent timestamps
4978 * ATM can only append one track at a time to the currently selected track
4980 static void trw_layer_append_track ( gpointer pass_along[6] )
4983 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4985 GHashTable *ght_tracks;
4986 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4987 ght_tracks = vtl->routes;
4989 ght_tracks = vtl->tracks;
4991 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4996 GList *other_tracks_names = NULL;
4998 // Sort alphabetically for user presentation
4999 // Convert into list of names for usage with dialog function
5000 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5002 udata.result = &other_tracks_names;
5003 udata.exclude = trk->trackpoints;
5005 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5007 // Note the limit to selecting one track only
5008 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5009 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5010 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5013 trk->is_route ? _("Append Route"): _("Append Track"),
5014 trk->is_route ? _("Select the route to append after the current route") :
5015 _("Select the track to append after the current track") );
5017 g_list_free(other_tracks_names);
5019 // It's a list, but shouldn't contain more than one other track!
5020 if ( append_list ) {
5022 for (l = append_list; l != NULL; l = g_list_next(l)) {
5023 // TODO: at present this uses the first track found by name,
5024 // which with potential multiple same named tracks may not be the one selected...
5025 VikTrack *append_track;
5026 if ( trk->is_route )
5027 append_track = vik_trw_layer_get_route ( vtl, l->data );
5029 append_track = vik_trw_layer_get_track ( vtl, l->data );
5031 if ( append_track ) {
5032 vik_track_steal_and_append_trackpoints ( trk, append_track );
5033 if ( trk->is_route )
5034 vik_trw_layer_delete_route (vtl, append_track);
5036 vik_trw_layer_delete_track (vtl, append_track);
5039 for (l = append_list; l != NULL; l = g_list_next(l))
5041 g_list_free(append_list);
5043 vik_layer_emit_update( VIK_LAYER(vtl) );
5048 * Very similar to trw_layer_append_track for joining
5049 * but this allows selection from the 'other' list
5050 * If a track is selected, then is shows routes and joins the selected one
5051 * If a route is selected, then is shows tracks and joins the selected one
5053 static void trw_layer_append_other ( gpointer pass_along[6] )
5056 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5058 GHashTable *ght_mykind, *ght_others;
5059 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5060 ght_mykind = vtl->routes;
5061 ght_others = vtl->tracks;
5064 ght_mykind = vtl->tracks;
5065 ght_others = vtl->routes;
5068 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5073 GList *other_tracks_names = NULL;
5075 // Sort alphabetically for user presentation
5076 // Convert into list of names for usage with dialog function
5077 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5079 udata.result = &other_tracks_names;
5080 udata.exclude = trk->trackpoints;
5082 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5084 // Note the limit to selecting one track only
5085 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5086 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5087 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5090 trk->is_route ? _("Append Track"): _("Append Route"),
5091 trk->is_route ? _("Select the track to append after the current route") :
5092 _("Select the route to append after the current track") );
5094 g_list_free(other_tracks_names);
5096 // It's a list, but shouldn't contain more than one other track!
5097 if ( append_list ) {
5099 for (l = append_list; l != NULL; l = g_list_next(l)) {
5100 // TODO: at present this uses the first track found by name,
5101 // which with potential multiple same named tracks may not be the one selected...
5103 // Get FROM THE OTHER TYPE list
5104 VikTrack *append_track;
5105 if ( trk->is_route )
5106 append_track = vik_trw_layer_get_track ( vtl, l->data );
5108 append_track = vik_trw_layer_get_route ( vtl, l->data );
5110 if ( append_track ) {
5112 if ( !append_track->is_route &&
5113 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5114 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5116 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5117 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5118 vik_track_merge_segments ( append_track );
5119 vik_track_to_routepoints ( append_track );
5126 vik_track_steal_and_append_trackpoints ( trk, append_track );
5128 // Delete copied which is FROM THE OTHER TYPE list
5129 if ( trk->is_route )
5130 vik_trw_layer_delete_track (vtl, append_track);
5132 vik_trw_layer_delete_route (vtl, append_track);
5135 for (l = append_list; l != NULL; l = g_list_next(l))
5137 g_list_free(append_list);
5138 vik_layer_emit_update( VIK_LAYER(vtl) );
5142 /* merge by segments */
5143 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5145 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5146 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5147 guint segments = vik_track_merge_segments ( trk );
5148 // NB currently no need to redraw as segments not actually shown on the display
5149 // However inform the user of what happened:
5151 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5152 g_snprintf(str, 64, tmp_str, segments);
5153 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5156 /* merge by time routine */
5157 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5159 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5163 GList *tracks_with_timestamp = NULL;
5164 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5165 if (orig_trk->trackpoints &&
5166 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5167 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5172 udata.result = &tracks_with_timestamp;
5173 udata.exclude = orig_trk->trackpoints;
5174 udata.with_timestamps = TRUE;
5175 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5176 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5178 if (!tracks_with_timestamp) {
5179 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5182 g_list_free(tracks_with_timestamp);
5184 static guint threshold_in_minutes = 1;
5185 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5186 _("Merge Threshold..."),
5187 _("Merge when time between tracks less than:"),
5188 &threshold_in_minutes)) {
5192 // keep attempting to merge all tracks until no merges within the time specified is possible
5193 gboolean attempt_merge = TRUE;
5194 GList *nearby_tracks = NULL;
5196 static gpointer params[3];
5198 while ( attempt_merge ) {
5200 // Don't try again unless tracks have changed
5201 attempt_merge = FALSE;
5203 trps = orig_trk->trackpoints;
5207 if (nearby_tracks) {
5208 g_list_free(nearby_tracks);
5209 nearby_tracks = NULL;
5212 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5213 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5215 /* g_print("Original track times: %d and %d\n", t1, t2); */
5216 params[0] = &nearby_tracks;
5217 params[1] = (gpointer)trps;
5218 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5220 /* get a list of adjacent-in-time tracks */
5221 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5224 GList *l = nearby_tracks;
5227 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5228 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5230 t1 = get_first_trackpoint(l)->timestamp;
5231 t2 = get_last_trackpoint(l)->timestamp;
5232 #undef get_first_trackpoint
5233 #undef get_last_trackpoint
5234 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5237 /* remove trackpoints from merged track, delete track */
5238 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5239 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5241 // Tracks have changed, therefore retry again against all the remaining tracks
5242 attempt_merge = TRUE;
5247 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5250 g_list_free(nearby_tracks);
5252 vik_layer_emit_update( VIK_LAYER(vtl) );
5256 * Split a track at the currently selected trackpoint
5258 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5260 if ( !vtl->current_tpl )
5263 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5264 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5266 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5267 GList *newglist = g_list_alloc ();
5268 newglist->prev = NULL;
5269 newglist->next = vtl->current_tpl->next;
5270 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5271 tr->trackpoints = newglist;
5273 vtl->current_tpl->next->prev = newglist; /* end old track here */
5274 vtl->current_tpl->next = NULL;
5276 // Bounds of the selected track changed due to the split
5277 vik_track_calculate_bounds ( vtl->current_tp_track );
5279 vtl->current_tpl = newglist; /* change tp to first of new track. */
5280 vtl->current_tp_track = tr;
5283 vik_trw_layer_add_route ( vtl, name, tr );
5285 vik_trw_layer_add_track ( vtl, name, tr );
5287 // Bounds of the new track created by the split
5288 vik_track_calculate_bounds ( tr );
5294 // Also need id of newly created track
5297 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5299 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5301 if ( trkf && udata.uuid )
5302 vtl->current_tp_id = udata.uuid;
5304 vtl->current_tp_id = NULL;
5306 vik_layer_emit_update(VIK_LAYER(vtl));
5312 /* split by time routine */
5313 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5315 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5316 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5317 GList *trps = track->trackpoints;
5319 GList *newlists = NULL;
5320 GList *newtps = NULL;
5321 static guint thr = 1;
5328 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5329 _("Split Threshold..."),
5330 _("Split when time between trackpoints exceeds:"),
5335 /* iterate through trackpoints, and copy them into new lists without touching original list */
5336 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5340 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5342 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5345 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5346 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5347 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5349 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5354 if (ts - prev_ts > thr*60) {
5355 /* flush accumulated trackpoints into new list */
5356 newlists = g_list_append(newlists, g_list_reverse(newtps));
5360 /* accumulate trackpoint copies in newtps, in reverse order */
5361 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5363 iter = g_list_next(iter);
5366 newlists = g_list_append(newlists, g_list_reverse(newtps));
5369 /* put lists of trackpoints into tracks */
5371 // Only bother updating if the split results in new tracks
5372 if (g_list_length (newlists) > 1) {
5377 tr = vik_track_copy ( track, FALSE );
5378 tr->trackpoints = (GList *)(iter->data);
5380 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5381 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5382 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5383 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5384 g_free ( new_tr_name );
5385 vik_track_calculate_bounds ( tr );
5386 iter = g_list_next(iter);
5388 // Remove original track and then update the display
5389 vik_trw_layer_delete_track (vtl, track);
5390 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5392 g_list_free(newlists);
5396 * Split a track by the number of points as specified by the user
5398 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5400 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5402 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5403 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5405 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5410 // Check valid track
5411 GList *trps = track->trackpoints;
5415 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5416 _("Split Every Nth Point"),
5417 _("Split on every Nth point:"),
5418 250, // Default value as per typical limited track capacity of various GPS devices
5422 // Was a valid number returned?
5428 GList *newlists = NULL;
5429 GList *newtps = NULL;
5434 /* accumulate trackpoint copies in newtps, in reverse order */
5435 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5437 if (count >= points) {
5438 /* flush accumulated trackpoints into new list */
5439 newlists = g_list_append(newlists, g_list_reverse(newtps));
5443 iter = g_list_next(iter);
5446 // If there is a remaining chunk put that into the new split list
5447 // This may well be the whole track if no split points were encountered
5449 newlists = g_list_append(newlists, g_list_reverse(newtps));
5452 /* put lists of trackpoints into tracks */
5454 // Only bother updating if the split results in new tracks
5455 if (g_list_length (newlists) > 1) {
5460 tr = vik_track_copy ( track, FALSE );
5461 tr->trackpoints = (GList *)(iter->data);
5463 if ( track->is_route ) {
5464 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5465 vik_trw_layer_add_route(vtl, new_tr_name, tr);
5468 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5469 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5471 g_free ( new_tr_name );
5472 vik_track_calculate_bounds ( tr );
5474 iter = g_list_next(iter);
5476 // Remove original track and then update the display
5477 if ( track->is_route )
5478 vik_trw_layer_delete_route (vtl, track);
5480 vik_trw_layer_delete_track (vtl, track);
5481 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5483 g_list_free(newlists);
5487 * Split a track at the currently selected trackpoint
5489 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5491 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5492 gint subtype = GPOINTER_TO_INT (pass_along[2]);
5493 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5497 * Split a track by its segments
5498 * Routes do not have segments so don't call this for routes
5500 static void trw_layer_split_segments ( gpointer pass_along[6] )
5502 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5503 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5510 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5513 for ( i = 0; i < ntracks; i++ ) {
5515 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5516 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5517 g_free ( new_tr_name );
5522 // Remove original track
5523 vik_trw_layer_delete_track ( vtl, trk );
5524 vik_layer_emit_update ( VIK_LAYER(vtl) );
5527 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5530 /* end of split/merge routines */
5533 * Delete adjacent track points at the same position
5534 * AKA Delete Dulplicates on the Properties Window
5536 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5538 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5540 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5541 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5543 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5548 gulong removed = vik_track_remove_dup_points ( trk );
5550 // Track has been updated so update tps:
5551 trw_layer_cancel_tps_of_track ( vtl, trk );
5553 // Inform user how much was deleted as it's not obvious from the normal view
5555 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5556 g_snprintf(str, 64, tmp_str, removed);
5557 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5559 vik_layer_emit_update ( VIK_LAYER(vtl) );
5563 * Delete adjacent track points with the same timestamp
5564 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5566 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5568 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5570 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5571 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5573 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5578 gulong removed = vik_track_remove_same_time_points ( trk );
5580 // Track has been updated so update tps:
5581 trw_layer_cancel_tps_of_track ( vtl, trk );
5583 // Inform user how much was deleted as it's not obvious from the normal view
5585 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5586 g_snprintf(str, 64, tmp_str, removed);
5587 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5589 vik_layer_emit_update ( VIK_LAYER(vtl) );
5595 static void trw_layer_reverse ( gpointer pass_along[6] )
5597 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5599 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5600 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5602 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5607 vik_track_reverse ( track );
5609 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5613 * Similar to trw_layer_enum_item, but this uses a sorted method
5616 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5618 GList **list = (GList**)udata;
5619 // *list = g_list_prepend(*all, key); //unsorted method
5620 // Sort named list alphabetically
5621 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5626 * Now Waypoint specific sort
5628 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5630 GList **list = (GList**)udata;
5631 // Sort named list alphabetically
5632 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5636 * Track specific sort
5638 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5640 GList **list = (GList**)udata;
5641 // Sort named list alphabetically
5642 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5647 gboolean has_same_track_name;
5648 const gchar *same_track_name;
5649 } same_track_name_udata;
5651 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5653 const gchar* namea = (const gchar*) aa;
5654 const gchar* nameb = (const gchar*) bb;
5657 gint result = strcmp ( namea, nameb );
5659 if ( result == 0 ) {
5660 // Found two names the same
5661 same_track_name_udata *user_data = udata;
5662 user_data->has_same_track_name = TRUE;
5663 user_data->same_track_name = namea;
5666 // Leave ordering the same
5671 * Find out if any tracks have the same name in this hash table
5673 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5675 // Sort items by name, then compare if any next to each other are the same
5677 GList *track_names = NULL;
5678 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5681 if ( ! track_names )
5684 same_track_name_udata udata;
5685 udata.has_same_track_name = FALSE;
5687 // Use sort routine to traverse list comparing items
5688 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5689 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5690 // Still no tracks...
5694 return udata.has_same_track_name;
5698 * Force unqiue track names for the track table specified
5699 * Note the panel is a required parameter to enable the update of the names displayed
5700 * Specify if on tracks or else on routes
5702 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5704 // . Search list for an instance of repeated name
5705 // . get track of this name
5706 // . create new name
5707 // . rename track & update equiv. treeview iter
5708 // . repeat until all different
5710 same_track_name_udata udata;
5712 GList *track_names = NULL;
5713 udata.has_same_track_name = FALSE;
5714 udata.same_track_name = NULL;
5716 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5719 if ( ! track_names )
5722 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5724 // Still no tracks...
5725 if ( ! dummy_list1 )
5728 while ( udata.has_same_track_name ) {
5730 // Find a track with the same name
5733 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5735 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5739 g_critical("Houston, we've had a problem.");
5740 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
5741 _("Internal Error in vik_trw_layer_uniquify_tracks") );
5746 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5747 vik_track_set_name ( trk, newname );
5753 // Need want key of it for treeview update
5754 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5756 if ( trkf && udataU.uuid ) {
5760 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5762 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5765 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5767 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
5769 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
5773 // Start trying to find same names again...
5775 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5776 udata.has_same_track_name = FALSE;
5777 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5779 // No tracks any more - give up searching
5780 if ( ! dummy_list2 )
5781 udata.has_same_track_name = FALSE;
5785 vik_layers_panel_emit_update ( vlp );
5788 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
5790 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5793 switch (GPOINTER_TO_INT (pass_along[2])) {
5794 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5795 iter = &(vtl->tracks_iter);
5796 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5798 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5799 iter = &(vtl->routes_iter);
5800 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5802 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5803 iter = &(vtl->waypoints_iter);
5804 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5808 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
5811 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
5813 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5816 switch (GPOINTER_TO_INT (pass_along[2])) {
5817 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5818 iter = &(vtl->tracks_iter);
5819 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5821 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5822 iter = &(vtl->routes_iter);
5823 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5825 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5826 iter = &(vtl->waypoints_iter);
5827 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5831 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
5837 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5839 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5842 // Ensure list of track names offered is unique
5843 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5844 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5845 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5846 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5852 // Sort list alphabetically for better presentation
5853 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5856 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5860 // Get list of items to delete from the user
5861 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5864 _("Delete Selection"),
5865 _("Select tracks to delete"));
5868 // Delete requested tracks
5869 // since specificly requested, IMHO no need for extra confirmation
5870 if ( delete_list ) {
5872 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5873 // This deletes first trk it finds of that name (but uniqueness is enforced above)
5874 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5876 g_list_free(delete_list);
5877 vik_layer_emit_update( VIK_LAYER(vtl) );
5884 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5886 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5889 // Ensure list of track names offered is unique
5890 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5891 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5892 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5893 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5899 // Sort list alphabetically for better presentation
5900 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5903 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5907 // Get list of items to delete from the user
5908 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5911 _("Delete Selection"),
5912 _("Select routes to delete") );
5915 // Delete requested routes
5916 // since specificly requested, IMHO no need for extra confirmation
5917 if ( delete_list ) {
5919 for (l = delete_list; l != NULL; l = g_list_next(l)) {
5920 // This deletes first route it finds of that name (but uniqueness is enforced above)
5921 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5923 g_list_free(delete_list);
5924 vik_layer_emit_update( VIK_LAYER(vtl) );
5929 gboolean has_same_waypoint_name;
5930 const gchar *same_waypoint_name;
5931 } same_waypoint_name_udata;
5933 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5935 const gchar* namea = (const gchar*) aa;
5936 const gchar* nameb = (const gchar*) bb;
5939 gint result = strcmp ( namea, nameb );
5941 if ( result == 0 ) {
5942 // Found two names the same
5943 same_waypoint_name_udata *user_data = udata;
5944 user_data->has_same_waypoint_name = TRUE;
5945 user_data->same_waypoint_name = namea;
5948 // Leave ordering the same
5953 * Find out if any waypoints have the same name in this layer
5955 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5957 // Sort items by name, then compare if any next to each other are the same
5959 GList *waypoint_names = NULL;
5960 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5963 if ( ! waypoint_names )
5966 same_waypoint_name_udata udata;
5967 udata.has_same_waypoint_name = FALSE;
5969 // Use sort routine to traverse list comparing items
5970 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5971 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5972 // Still no waypoints...
5976 return udata.has_same_waypoint_name;
5980 * Force unqiue waypoint names for this layer
5981 * Note the panel is a required parameter to enable the update of the names displayed
5983 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5985 // . Search list for an instance of repeated name
5986 // . get waypoint of this name
5987 // . create new name
5988 // . rename waypoint & update equiv. treeview iter
5989 // . repeat until all different
5991 same_waypoint_name_udata udata;
5993 GList *waypoint_names = NULL;
5994 udata.has_same_waypoint_name = FALSE;
5995 udata.same_waypoint_name = NULL;
5997 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6000 if ( ! waypoint_names )
6003 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6005 // Still no waypoints...
6006 if ( ! dummy_list1 )
6009 while ( udata.has_same_waypoint_name ) {
6011 // Find a waypoint with the same name
6012 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6016 g_critical("Houston, we've had a problem.");
6017 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6018 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6023 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6025 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6027 // Start trying to find same names again...
6028 waypoint_names = NULL;
6029 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6030 udata.has_same_waypoint_name = FALSE;
6031 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6033 // No waypoints any more - give up searching
6034 if ( ! dummy_list2 )
6035 udata.has_same_waypoint_name = FALSE;
6039 vik_layers_panel_emit_update ( vlp );
6045 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6047 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6050 // Ensure list of waypoint names offered is unique
6051 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6052 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6053 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6054 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6060 // Sort list alphabetically for better presentation
6061 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6063 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6067 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6069 // Get list of items to delete from the user
6070 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6073 _("Delete Selection"),
6074 _("Select waypoints to delete"));
6077 // Delete requested waypoints
6078 // since specificly requested, IMHO no need for extra confirmation
6079 if ( delete_list ) {
6081 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6082 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6083 trw_layer_delete_waypoint_by_name (vtl, l->data);
6085 g_list_free(delete_list);
6087 trw_layer_calculate_bounds_waypoints ( vtl );
6088 vik_layer_emit_update( VIK_LAYER(vtl) );
6096 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6098 vik_treeview_item_toggle_visible ( vt, it );
6104 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6106 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6112 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6114 wp->visible = GPOINTER_TO_INT (on_off);
6120 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6122 wp->visible = !wp->visible;
6128 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6130 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6131 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6132 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6133 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6135 vik_layer_emit_update ( VIK_LAYER(vtl) );
6141 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6143 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6144 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6145 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6146 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6148 vik_layer_emit_update ( VIK_LAYER(vtl) );
6154 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6156 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6157 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6158 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6160 vik_layer_emit_update ( VIK_LAYER(vtl) );
6166 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6168 trk->visible = GPOINTER_TO_INT (on_off);
6174 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6176 trk->visible = !trk->visible;
6182 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6184 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6185 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6186 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6187 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6189 vik_layer_emit_update ( VIK_LAYER(vtl) );
6195 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6197 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6198 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6199 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6200 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6202 vik_layer_emit_update ( VIK_LAYER(vtl) );
6208 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6210 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6211 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6212 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6214 vik_layer_emit_update ( VIK_LAYER(vtl) );
6220 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6222 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6223 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6224 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6225 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6227 vik_layer_emit_update ( VIK_LAYER(vtl) );
6233 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6235 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6236 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6237 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6238 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6240 vik_layer_emit_update ( VIK_LAYER(vtl) );
6246 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6248 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6249 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6250 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6252 vik_layer_emit_update ( VIK_LAYER(vtl) );
6256 * trw_layer_analyse_close:
6258 * Stuff to do on dialog closure
6260 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6262 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6263 gtk_widget_destroy ( dialog );
6264 vtl->tracks_analysis_dialog = NULL;
6268 * trw_layer_analyse_create_list:
6270 * Create the latest list of tracks with the associated layer(s)
6271 * Although this will always be from a single layer here
6273 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6275 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6276 GList *tracks = NULL;
6277 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6278 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6280 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6282 GList *tracks_and_layers = NULL;
6283 // build tracks_and_layers list
6284 tracks = g_list_first ( tracks );
6286 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6287 vtdl->trk = VIK_TRACK(tracks->data);
6289 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6290 tracks = g_list_next ( tracks );
6293 return tracks_and_layers;
6296 static void trw_layer_tracks_stats ( gpointer lav[2] )
6298 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6299 // There can only be one!
6300 if ( vtl->tracks_analysis_dialog )
6303 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6304 VIK_LAYER(vtl)->name,
6306 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6307 trw_layer_analyse_create_list,
6308 trw_layer_analyse_close );
6314 static void trw_layer_routes_stats ( gpointer lav[2] )
6316 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6317 // There can only be one!
6318 if ( vtl->tracks_analysis_dialog )
6321 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6322 VIK_LAYER(vtl)->name,
6324 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6325 trw_layer_analyse_create_list,
6326 trw_layer_analyse_close );
6329 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6331 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6333 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6336 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6338 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6341 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6342 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6346 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6348 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6351 if ( !strncmp(wp->comment, "http", 4) ) {
6352 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6353 } else if ( !strncmp(wp->description, "http", 4) ) {
6354 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6358 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6360 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6362 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6364 // No actual change to the name supplied
6366 if (strcmp(newname, wp->name) == 0 )
6369 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6372 // An existing waypoint has been found with the requested name
6373 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6374 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6379 // Update WP name and refresh the treeview
6380 vik_waypoint_set_name (wp, newname);
6382 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6383 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6385 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6390 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6392 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6394 // No actual change to the name supplied
6396 if (strcmp(newname, trk->name) == 0)
6399 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6402 // An existing track has been found with the requested name
6403 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6404 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6408 // Update track name and refresh GUI parts
6409 vik_track_set_name (trk, newname);
6411 // Update any subwindows that could be displaying this track which has changed name
6412 // Only one Track Edit Window
6413 if ( l->current_tp_track == trk && l->tpwin ) {
6414 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6416 // Property Dialog of the track
6417 vik_trw_layer_propwin_update ( trk );
6419 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6420 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6422 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6427 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6429 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6431 // No actual change to the name supplied
6433 if (strcmp(newname, trk->name) == 0)
6436 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6439 // An existing track has been found with the requested name
6440 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6441 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6445 // Update track name and refresh GUI parts
6446 vik_track_set_name (trk, newname);
6448 // Update any subwindows that could be displaying this track which has changed name
6449 // Only one Track Edit Window
6450 if ( l->current_tp_track == trk && l->tpwin ) {
6451 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6453 // Property Dialog of the track
6454 vik_trw_layer_propwin_update ( trk );
6456 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6457 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6459 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6466 static gboolean is_valid_geocache_name ( gchar *str )
6468 gint len = strlen ( str );
6469 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]));
6472 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6474 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6475 a_acquire_set_filter_track ( trk );
6478 #ifdef VIK_CONFIG_GOOGLE
6479 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6481 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6482 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6485 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6487 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6489 gchar *escaped = uri_escape ( tr->comment );
6490 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6491 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6498 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6499 /* viewpoint is now available instead */
6500 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6502 static gpointer pass_along[8];
6504 gboolean rv = FALSE;
6507 pass_along[1] = vlp;
6508 pass_along[2] = GINT_TO_POINTER (subtype);
6509 pass_along[3] = sublayer;
6510 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6511 pass_along[5] = vvp;
6512 pass_along[6] = iter;
6513 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6515 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6519 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6520 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6521 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6522 gtk_widget_show ( item );
6524 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6525 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6526 if (tr && tr->property_dialog)
6527 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6529 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6530 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6531 if (tr && tr->property_dialog)
6532 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6535 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6537 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6538 gtk_widget_show ( item );
6540 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6541 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6542 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6543 gtk_widget_show ( item );
6545 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6546 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6547 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6548 gtk_widget_show ( item );
6550 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6552 gboolean separator_created = FALSE;
6554 /* could be a right-click using the tool */
6555 if ( vlp != NULL ) {
6556 item = gtk_menu_item_new ();
6557 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6558 gtk_widget_show ( item );
6560 separator_created = TRUE;
6562 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6563 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6564 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6565 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6566 gtk_widget_show ( item );
6569 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6571 if ( wp && wp->name ) {
6572 if ( is_valid_geocache_name ( wp->name ) ) {
6574 if ( !separator_created ) {
6575 item = gtk_menu_item_new ();
6576 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6577 gtk_widget_show ( item );
6578 separator_created = TRUE;
6581 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6582 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6583 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6584 gtk_widget_show ( item );
6588 if ( wp && wp->image )
6590 if ( !separator_created ) {
6591 item = gtk_menu_item_new ();
6592 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6593 gtk_widget_show ( item );
6594 separator_created = TRUE;
6597 // Set up image paramater
6598 pass_along[5] = wp->image;
6600 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6601 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6602 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6603 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6604 gtk_widget_show ( item );
6606 #ifdef VIK_CONFIG_GEOTAG
6607 GtkWidget *geotag_submenu = gtk_menu_new ();
6608 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6609 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6610 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6611 gtk_widget_show ( item );
6612 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6614 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6615 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6616 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6617 gtk_widget_show ( item );
6619 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6621 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6622 gtk_widget_show ( item );
6628 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6629 ( wp->description && !strncmp(wp->description, "http", 4) )) {
6630 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6631 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6632 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6633 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6634 gtk_widget_show ( item );
6641 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6642 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6643 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6644 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6645 gtk_widget_show ( item );
6646 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6647 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6648 gtk_widget_set_sensitive ( item, TRUE );
6650 gtk_widget_set_sensitive ( item, FALSE );
6653 item = gtk_menu_item_new ();
6654 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6655 gtk_widget_show ( item );
6658 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6661 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6662 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6664 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6665 gtk_widget_show ( item );
6668 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6670 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6671 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6672 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6673 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6674 gtk_widget_show ( item );
6676 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6677 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6679 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6680 gtk_widget_show ( item );
6682 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6683 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6684 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6685 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6686 gtk_widget_show ( item );
6688 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6689 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6691 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6692 gtk_widget_show ( item );
6694 GtkWidget *vis_submenu = gtk_menu_new ();
6695 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6696 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6697 gtk_widget_show ( item );
6698 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6700 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
6701 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
6703 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6704 gtk_widget_show ( item );
6706 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
6707 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6708 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
6709 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6710 gtk_widget_show ( item );
6712 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6713 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6714 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
6715 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6716 gtk_widget_show ( item );
6719 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6723 if ( l->current_track && !l->current_track->is_route ) {
6724 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6726 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6727 gtk_widget_show ( item );
6729 item = gtk_menu_item_new ();
6730 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6731 gtk_widget_show ( item );
6734 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6735 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6736 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6737 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6738 gtk_widget_show ( item );
6740 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6741 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6743 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6744 gtk_widget_show ( item );
6745 // Make it available only when a new track *not* already in progress
6746 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6748 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6749 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6750 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6751 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6752 gtk_widget_show ( item );
6754 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6755 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6756 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6757 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6758 gtk_widget_show ( item );
6760 GtkWidget *vis_submenu = gtk_menu_new ();
6761 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6762 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6763 gtk_widget_show ( item );
6764 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6766 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
6767 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6768 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
6769 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6770 gtk_widget_show ( item );
6772 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
6773 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
6775 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6776 gtk_widget_show ( item );
6778 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6779 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
6781 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6782 gtk_widget_show ( item );
6784 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
6786 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6787 gtk_widget_show ( item );
6790 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6794 if ( l->current_track && l->current_track->is_route ) {
6795 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6796 // Reuse finish track method
6797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6798 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6799 gtk_widget_show ( item );
6801 item = gtk_menu_item_new ();
6802 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6803 gtk_widget_show ( item );
6806 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6807 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6809 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6810 gtk_widget_show ( item );
6812 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6813 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6814 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6815 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6816 gtk_widget_show ( item );
6817 // Make it available only when a new track *not* already in progress
6818 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6820 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6823 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6824 gtk_widget_show ( item );
6826 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6827 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6828 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6829 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6830 gtk_widget_show ( item );
6832 GtkWidget *vis_submenu = gtk_menu_new ();
6833 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6834 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6835 gtk_widget_show ( item );
6836 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6838 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
6839 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6840 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
6841 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6842 gtk_widget_show ( item );
6844 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
6845 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6846 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
6847 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6848 gtk_widget_show ( item );
6850 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6851 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6852 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
6853 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6854 gtk_widget_show ( item );
6856 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6857 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
6858 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6859 gtk_widget_show ( item );
6863 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6864 GtkWidget *submenu_sort = gtk_menu_new ();
6865 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
6866 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6867 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6868 gtk_widget_show ( item );
6869 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
6871 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
6872 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
6873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
6874 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
6875 gtk_widget_show ( item );
6877 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
6878 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
6879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
6880 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
6881 gtk_widget_show ( item );
6884 GtkWidget *upload_submenu = gtk_menu_new ();
6886 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6888 item = gtk_menu_item_new ();
6889 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6890 gtk_widget_show ( item );
6892 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6893 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6894 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6895 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6896 if ( l->current_track ) {
6897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6898 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6899 gtk_widget_show ( item );
6902 item = gtk_menu_item_new ();
6903 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6904 gtk_widget_show ( item );
6907 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6908 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6910 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6911 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6912 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6913 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6914 gtk_widget_show ( item );
6916 GtkWidget *goto_submenu;
6917 goto_submenu = gtk_menu_new ();
6918 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6919 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6920 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6921 gtk_widget_show ( item );
6922 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6924 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6925 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6927 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6928 gtk_widget_show ( item );
6930 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6931 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6932 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6933 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6934 gtk_widget_show ( item );
6936 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6937 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6938 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6939 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6940 gtk_widget_show ( item );
6942 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6943 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6945 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6946 gtk_widget_show ( item );
6948 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6949 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6951 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6952 gtk_widget_show ( item );
6954 // Routes don't have speeds
6955 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6956 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6957 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6958 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6959 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6960 gtk_widget_show ( item );
6963 GtkWidget *combine_submenu;
6964 combine_submenu = gtk_menu_new ();
6965 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6967 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6968 gtk_widget_show ( item );
6969 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6971 // Routes don't have times or segments...
6972 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6973 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6975 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6976 gtk_widget_show ( item );
6978 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6980 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6981 gtk_widget_show ( item );
6984 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6986 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6987 gtk_widget_show ( item );
6989 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6990 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6992 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6994 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6995 gtk_widget_show ( item );
6997 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6998 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7000 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7002 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7003 gtk_widget_show ( item );
7005 GtkWidget *split_submenu;
7006 split_submenu = gtk_menu_new ();
7007 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7008 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7010 gtk_widget_show ( item );
7011 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7013 // Routes don't have times or segments...
7014 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7015 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7016 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7017 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7018 gtk_widget_show ( item );
7020 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7021 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7023 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7024 gtk_widget_show ( item );
7027 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7029 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7030 gtk_widget_show ( item );
7032 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7034 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7035 gtk_widget_show ( item );
7036 // Make it available only when a trackpoint is selected.
7037 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7039 GtkWidget *delete_submenu;
7040 delete_submenu = gtk_menu_new ();
7041 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7042 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7043 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7044 gtk_widget_show ( item );
7045 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7047 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7049 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7050 gtk_widget_show ( item );
7052 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7054 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7055 gtk_widget_show ( item );
7057 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7058 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7060 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7061 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7063 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7064 gtk_widget_show ( item );
7066 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7068 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7069 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7071 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7074 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7075 gtk_widget_show ( item );
7078 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7079 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
7081 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7082 gtk_widget_show ( item );
7084 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7085 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7087 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7090 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7091 gtk_widget_show ( item );
7093 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7094 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7096 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7098 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7099 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7100 gtk_widget_show ( item );
7102 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7103 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7105 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7108 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7109 gtk_widget_show ( item );
7111 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7112 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7113 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7115 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7116 gtk_widget_show ( item );
7119 // ATM can't upload a single waypoint but can do waypoints to a GPS
7120 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7121 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7123 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7124 gtk_widget_show ( item );
7125 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7127 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7128 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7129 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7130 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7131 gtk_widget_show ( item );
7135 #ifdef VIK_CONFIG_GOOGLE
7136 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7138 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7141 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7142 gtk_widget_show ( item );
7146 // Some things aren't usable with routes
7147 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7148 #ifdef VIK_CONFIG_OPENSTREETMAP
7149 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7150 // Convert internal pointer into actual track for usage outside this file
7151 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7152 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7154 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7155 gtk_widget_show ( item );
7158 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7159 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7161 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7162 gtk_widget_show ( item );
7164 /* ATM This function is only available via the layers panel, due to needing a vlp */
7166 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7167 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7168 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7171 gtk_widget_show ( item );
7175 #ifdef VIK_CONFIG_GEOTAG
7176 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7177 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7178 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7179 gtk_widget_show ( item );
7183 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7184 // Only show on viewport popmenu when a trackpoint is selected
7185 if ( ! vlp && l->current_tpl ) {
7187 item = gtk_menu_item_new ();
7188 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7189 gtk_widget_show ( item );
7191 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7192 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7193 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7194 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7195 gtk_widget_show ( item );
7202 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7205 if (!vtl->current_tpl)
7207 if (!vtl->current_tpl->next)
7210 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7211 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7213 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7216 VikTrackpoint *tp_new = vik_trackpoint_new();
7217 struct LatLon ll_current, ll_next;
7218 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7219 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7221 /* main positional interpolation */
7222 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7223 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7225 /* Now other properties that can be interpolated */
7226 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7228 if (tp_current->has_timestamp && tp_next->has_timestamp) {
7229 /* Note here the division is applied to each part, then added
7230 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7231 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7232 tp_new->has_timestamp = TRUE;
7235 if (tp_current->speed != NAN && tp_next->speed != NAN)
7236 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7238 /* TODO - improve interpolation of course, as it may not be correct.
7239 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7240 [similar applies if value is in radians] */
7241 if (tp_current->course != NAN && tp_next->course != NAN)
7242 tp_new->course = (tp_current->course + tp_next->course)/2;
7244 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7246 /* Insert new point into the trackpoints list after the current TP */
7247 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7249 // Otherwise try routes
7250 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7254 gint index = g_list_index ( trk->trackpoints, tp_current );
7256 // NB no recalculation of bounds since it is inserted between points
7257 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7262 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7268 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7272 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7274 if ( vtl->current_tpl )
7276 vtl->current_tpl = NULL;
7277 vtl->current_tp_track = NULL;
7278 vtl->current_tp_id = NULL;
7279 vik_layer_emit_update(VIK_LAYER(vtl));
7283 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7285 g_assert ( vtl->tpwin != NULL );
7286 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7287 trw_layer_cancel_current_tp ( vtl, TRUE );
7289 if ( vtl->current_tpl == NULL )
7292 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7294 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7295 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7297 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7299 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7301 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7307 // Find available adjacent trackpoint
7308 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7310 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7311 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7313 // Delete current trackpoint
7314 vik_trackpoint_free ( vtl->current_tpl->data );
7315 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7317 // Set to current to the available adjacent trackpoint
7318 vtl->current_tpl = new_tpl;
7320 // Reset dialog with the available adjacent trackpoint
7321 if ( vtl->current_tp_track ) {
7322 vik_track_calculate_bounds ( vtl->current_tp_track );
7323 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7326 vik_layer_emit_update(VIK_LAYER(vtl));
7330 // Delete current trackpoint
7331 vik_trackpoint_free ( vtl->current_tpl->data );
7332 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7333 trw_layer_cancel_current_tp ( vtl, FALSE );
7336 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7338 if ( vtl->current_tp_track )
7339 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7340 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7342 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7344 if ( vtl->current_tp_track )
7345 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7346 vik_layer_emit_update(VIK_LAYER(vtl));
7348 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7350 trw_layer_insert_tp_after_current_tp ( vtl );
7351 vik_layer_emit_update(VIK_LAYER(vtl));
7353 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7354 vik_layer_emit_update(VIK_LAYER(vtl));
7358 * trw_layer_dialog_shift:
7359 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7361 * Try to reposition a dialog if it's over the specified coord
7362 * so to not obscure the item of interest
7364 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7366 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
7368 // Attempt force dialog to be shown so we can find out where it is more reliably...
7369 while ( gtk_events_pending() )
7370 gtk_main_iteration ();
7372 // get parent window position & size
7373 gint win_pos_x, win_pos_y;
7374 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
7376 gint win_size_x, win_size_y;
7377 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
7379 // get own dialog size
7380 gint dia_size_x, dia_size_y;
7381 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
7383 // get own dialog position
7384 gint dia_pos_x, dia_pos_y;
7385 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
7387 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
7388 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
7390 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
7392 gint vp_xx, vp_yy; // In viewport pixels
7393 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
7395 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
7399 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
7401 // Transform Viewport pixels into absolute pixels
7402 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
7403 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
7405 // Is dialog over the point (to within an ^^ edge value)
7406 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
7407 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
7411 gint hh = vik_viewport_get_height ( vvp );
7413 // Consider the difference in viewport to the full window
7414 gint offset_y = dest_y;
7415 // Add difference between dialog and window sizes
7416 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
7418 if ( vp_yy > hh/2 ) {
7419 // Point in bottom half, move window to top half
7420 gtk_window_move ( dialog, dia_pos_x, offset_y );
7423 // Point in top half, move dialog down
7424 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
7428 // Shift left<->right
7429 gint ww = vik_viewport_get_width ( vvp );
7431 // Consider the difference in viewport to the full window
7432 gint offset_x = dest_x;
7433 // Add difference between dialog and window sizes
7434 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
7436 if ( vp_xx > ww/2 ) {
7437 // Point on right, move window to left
7438 gtk_window_move ( dialog, offset_x, dia_pos_y );
7441 // Point on left, move right
7442 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7450 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7454 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7455 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7456 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7457 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7459 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7461 if ( vtl->current_tpl ) {
7462 // get tp pixel position
7463 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7465 // Shift up<->down to try not to obscure the trackpoint.
7466 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7470 if ( vtl->current_tpl )
7471 if ( vtl->current_tp_track )
7472 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7473 /* set layer name and TP data */
7476 /***************************************************************************
7478 ***************************************************************************/
7480 /*** Utility data structures and functions ****/
7484 gint closest_x, closest_y;
7485 gboolean draw_images;
7486 gpointer *closest_wp_id;
7487 VikWaypoint *closest_wp;
7493 gint closest_x, closest_y;
7494 gpointer closest_track_id;
7495 VikTrackpoint *closest_tp;
7501 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7507 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7509 // If waypoint has an image then use the image size to select
7510 if ( params->draw_images && wp->image ) {
7511 gint slackx, slacky;
7512 slackx = wp->image_width / 2;
7513 slacky = wp->image_height / 2;
7515 if ( x <= params->x + slackx && x >= params->x - slackx
7516 && y <= params->y + slacky && y >= params->y - slacky ) {
7517 params->closest_wp_id = id;
7518 params->closest_wp = wp;
7519 params->closest_x = x;
7520 params->closest_y = y;
7523 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7524 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
7525 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7527 params->closest_wp_id = id;
7528 params->closest_wp = wp;
7529 params->closest_x = x;
7530 params->closest_y = y;
7534 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7536 GList *tpl = t->trackpoints;
7542 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7548 tp = VIK_TRACKPOINT(tpl->data);
7550 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7552 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7553 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
7554 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7556 params->closest_track_id = id;
7557 params->closest_tp = tp;
7558 params->closest_tpl = tpl;
7559 params->closest_x = x;
7560 params->closest_y = y;
7566 // ATM: Leave this as 'Track' only.
7567 // Not overly bothered about having a snap to route trackpoint capability
7568 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7570 TPSearchParams params;
7574 params.closest_track_id = NULL;
7575 params.closest_tp = NULL;
7576 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7577 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
7578 return params.closest_tp;
7581 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7583 WPSearchParams params;
7587 params.draw_images = vtl->drawimages;
7588 params.closest_wp = NULL;
7589 params.closest_wp_id = NULL;
7590 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
7591 return params.closest_wp;
7595 // Some forward declarations
7596 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7597 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7598 static void marker_end_move ( tool_ed_t *t );
7601 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7605 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7607 // Here always allow snapping back to the original location
7608 // this is useful when one decides not to move the thing afterall
7609 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7612 if ( event->state & GDK_CONTROL_MASK )
7614 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7616 new_coord = tp->coord;
7620 if ( event->state & GDK_SHIFT_MASK )
7622 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7624 new_coord = wp->coord;
7628 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7630 marker_moveto ( t, x, y );
7637 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7639 if ( t->holding && event->button == 1 )
7642 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7645 if ( event->state & GDK_CONTROL_MASK )
7647 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7649 new_coord = tp->coord;
7653 if ( event->state & GDK_SHIFT_MASK )
7655 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7657 new_coord = wp->coord;
7660 marker_end_move ( t );
7662 // Determine if working on a waypoint or a trackpoint
7663 if ( t->is_waypoint ) {
7664 vtl->current_wp->coord = new_coord;
7665 trw_layer_calculate_bounds_waypoints ( vtl );
7668 if ( vtl->current_tpl ) {
7669 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7671 if ( vtl->current_tp_track )
7672 vik_track_calculate_bounds ( vtl->current_tp_track );
7675 if ( vtl->current_tp_track )
7676 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7681 vtl->current_wp = NULL;
7682 vtl->current_wp_id = NULL;
7683 trw_layer_cancel_current_tp ( vtl, FALSE );
7685 vik_layer_emit_update ( VIK_LAYER(vtl) );
7692 Returns true if a waypoint or track is found near the requested event position for this particular layer
7693 The item found is automatically selected
7694 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7696 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7698 if ( event->button != 1 )
7701 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7704 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7708 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7710 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7712 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7713 WPSearchParams wp_params;
7714 wp_params.vvp = vvp;
7715 wp_params.x = event->x;
7716 wp_params.y = event->y;
7717 wp_params.draw_images = vtl->drawimages;
7718 wp_params.closest_wp_id = NULL;
7719 wp_params.closest_wp = NULL;
7721 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7723 if ( wp_params.closest_wp ) {
7726 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7728 // Too easy to move it so must be holding shift to start immediately moving it
7729 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7730 if ( event->state & GDK_SHIFT_MASK ||
7731 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7732 // Put into 'move buffer'
7733 // NB vvp & vw already set in tet
7734 tet->vtl = (gpointer)vtl;
7735 tet->is_waypoint = TRUE;
7737 marker_begin_move (tet, event->x, event->y);
7740 vtl->current_wp = wp_params.closest_wp;
7741 vtl->current_wp_id = wp_params.closest_wp_id;
7743 vik_layer_emit_update ( VIK_LAYER(vtl) );
7749 // Used for both track and route lists
7750 TPSearchParams tp_params;
7751 tp_params.vvp = vvp;
7752 tp_params.x = event->x;
7753 tp_params.y = event->y;
7754 tp_params.closest_track_id = NULL;
7755 tp_params.closest_tp = NULL;
7756 tp_params.closest_tpl = NULL;
7757 tp_params.bbox = bbox;
7759 if (vtl->tracks_visible) {
7760 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7762 if ( tp_params.closest_tp ) {
7764 // Always select + highlight the track
7765 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7767 tet->is_waypoint = FALSE;
7769 // Select the Trackpoint
7770 // Can move it immediately when control held or it's the previously selected tp
7771 if ( event->state & GDK_CONTROL_MASK ||
7772 vtl->current_tpl == tp_params.closest_tpl ) {
7773 // Put into 'move buffer'
7774 // NB vvp & vw already set in tet
7775 tet->vtl = (gpointer)vtl;
7776 marker_begin_move (tet, event->x, event->y);
7779 vtl->current_tpl = tp_params.closest_tpl;
7780 vtl->current_tp_id = tp_params.closest_track_id;
7781 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7783 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7786 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7788 vik_layer_emit_update ( VIK_LAYER(vtl) );
7793 // Try again for routes
7794 if (vtl->routes_visible) {
7795 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7797 if ( tp_params.closest_tp ) {
7799 // Always select + highlight the track
7800 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7802 tet->is_waypoint = FALSE;
7804 // Select the Trackpoint
7805 // Can move it immediately when control held or it's the previously selected tp
7806 if ( event->state & GDK_CONTROL_MASK ||
7807 vtl->current_tpl == tp_params.closest_tpl ) {
7808 // Put into 'move buffer'
7809 // NB vvp & vw already set in tet
7810 tet->vtl = (gpointer)vtl;
7811 marker_begin_move (tet, event->x, event->y);
7814 vtl->current_tpl = tp_params.closest_tpl;
7815 vtl->current_tp_id = tp_params.closest_track_id;
7816 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7818 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7821 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7823 vik_layer_emit_update ( VIK_LAYER(vtl) );
7828 /* these aren't the droids you're looking for */
7829 vtl->current_wp = NULL;
7830 vtl->current_wp_id = NULL;
7831 trw_layer_cancel_current_tp ( vtl, FALSE );
7834 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7839 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7841 if ( event->button != 3 )
7844 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7847 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7850 /* Post menu for the currently selected item */
7852 /* See if a track is selected */
7853 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7854 if ( track && track->visible ) {
7856 if ( track->name ) {
7858 if ( vtl->track_right_click_menu )
7859 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7861 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7868 if ( track->is_route )
7869 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7871 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7873 if ( trkf && udataU.uuid ) {
7876 if ( track->is_route )
7877 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7879 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7881 trw_layer_sublayer_add_menu_items ( vtl,
7882 vtl->track_right_click_menu,
7884 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7890 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7896 /* See if a waypoint is selected */
7897 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7898 if ( waypoint && waypoint->visible ) {
7899 if ( waypoint->name ) {
7901 if ( vtl->wp_right_click_menu )
7902 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7904 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7907 udata.wp = waypoint;
7910 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7912 if ( wpf && udata.uuid ) {
7913 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7915 trw_layer_sublayer_add_menu_items ( vtl,
7916 vtl->wp_right_click_menu,
7918 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7923 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7932 /* background drawing hook, to be passed the viewport */
7933 static gboolean tool_sync_done = TRUE;
7935 static gboolean tool_sync(gpointer data)
7937 VikViewport *vvp = data;
7938 gdk_threads_enter();
7939 vik_viewport_sync(vvp);
7940 tool_sync_done = TRUE;
7941 gdk_threads_leave();
7945 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7948 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7949 gdk_gc_set_function ( t->gc, GDK_INVERT );
7950 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7951 vik_viewport_sync(t->vvp);
7956 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7958 VikViewport *vvp = t->vvp;
7959 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7960 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7964 if (tool_sync_done) {
7965 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7966 tool_sync_done = FALSE;
7970 static void marker_end_move ( tool_ed_t *t )
7972 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7973 g_object_unref ( t->gc );
7977 /*** Edit waypoint ****/
7979 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7981 tool_ed_t *t = g_new(tool_ed_t, 1);
7987 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
7992 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7994 WPSearchParams params;
7995 tool_ed_t *t = data;
7996 VikViewport *vvp = t->vvp;
7998 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8005 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8008 if ( vtl->current_wp && vtl->current_wp->visible )
8010 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8012 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8014 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8015 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8017 if ( event->button == 3 )
8018 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8020 marker_begin_move(t, event->x, event->y);
8027 params.x = event->x;
8028 params.y = event->y;
8029 params.draw_images = vtl->drawimages;
8030 params.closest_wp_id = NULL;
8031 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8032 params.closest_wp = NULL;
8033 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8034 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8036 // how do we get here?
8037 marker_begin_move(t, event->x, event->y);
8038 g_critical("shouldn't be here");
8041 else if ( params.closest_wp )
8043 if ( event->button == 3 )
8044 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8046 vtl->waypoint_rightclick = FALSE;
8048 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8050 vtl->current_wp = params.closest_wp;
8051 vtl->current_wp_id = params.closest_wp_id;
8053 /* could make it so don't update if old WP is off screen and new is null but oh well */
8054 vik_layer_emit_update ( VIK_LAYER(vtl) );
8058 vtl->current_wp = NULL;
8059 vtl->current_wp_id = NULL;
8060 vtl->waypoint_rightclick = FALSE;
8061 vik_layer_emit_update ( VIK_LAYER(vtl) );
8065 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8067 tool_ed_t *t = data;
8068 VikViewport *vvp = t->vvp;
8070 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8075 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8078 if ( event->state & GDK_CONTROL_MASK )
8080 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8082 new_coord = tp->coord;
8086 if ( event->state & GDK_SHIFT_MASK )
8088 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8089 if ( wp && wp != vtl->current_wp )
8090 new_coord = wp->coord;
8095 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8097 marker_moveto ( t, x, y );
8104 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8106 tool_ed_t *t = data;
8107 VikViewport *vvp = t->vvp;
8109 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8112 if ( t->holding && event->button == 1 )
8115 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8118 if ( event->state & GDK_CONTROL_MASK )
8120 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8122 new_coord = tp->coord;
8126 if ( event->state & GDK_SHIFT_MASK )
8128 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8129 if ( wp && wp != vtl->current_wp )
8130 new_coord = wp->coord;
8133 marker_end_move ( t );
8135 vtl->current_wp->coord = new_coord;
8137 trw_layer_calculate_bounds_waypoints ( vtl );
8138 vik_layer_emit_update ( VIK_LAYER(vtl) );
8141 /* PUT IN RIGHT PLACE!!! */
8142 if ( event->button == 3 && vtl->waypoint_rightclick )
8144 if ( vtl->wp_right_click_menu )
8145 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8146 if ( vtl->current_wp ) {
8147 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8148 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 );
8149 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8151 vtl->waypoint_rightclick = FALSE;
8156 /*** New track ****/
8158 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8165 GdkDrawable *drawable;
8171 * Draw specified pixmap
8173 static gboolean draw_sync ( gpointer data )
8175 draw_sync_t *ds = (draw_sync_t*) data;
8176 // Sometimes don't want to draw
8177 // normally because another update has taken precedent such as panning the display
8178 // which means this pixmap is no longer valid
8179 if ( ds->vtl->draw_sync_do ) {
8180 gdk_threads_enter();
8181 gdk_draw_drawable (ds->drawable,
8184 0, 0, 0, 0, -1, -1);
8185 ds->vtl->draw_sync_done = TRUE;
8186 gdk_threads_leave();
8192 static gchar* distance_string (gdouble distance)
8196 /* draw label with distance */
8197 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8198 switch (dist_units) {
8199 case VIK_UNITS_DISTANCE_MILES:
8200 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8201 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8202 } else if (distance < 1609.4) {
8203 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8205 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8209 // VIK_UNITS_DISTANCE_KILOMETRES
8210 if (distance >= 1000 && distance < 100000) {
8211 g_sprintf(str, "%3.2f km", distance/1000.0);
8212 } else if (distance < 1000) {
8213 g_sprintf(str, "%d m", (int)distance);
8215 g_sprintf(str, "%d km", (int)distance/1000);
8219 return g_strdup (str);
8223 * Actually set the message in statusbar
8225 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8227 // Only show elevation data when track has some elevation properties
8228 gchar str_gain_loss[64];
8229 str_gain_loss[0] = '\0';
8230 gchar str_last_step[64];
8231 str_last_step[0] = '\0';
8232 gchar *str_total = distance_string (distance);
8234 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8235 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8236 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8238 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8241 if ( last_step > 0 ) {
8242 gchar *tmp = distance_string (last_step);
8243 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8247 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8249 // Write with full gain/loss information
8250 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8251 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8253 g_free ( str_total );
8257 * Figure out what information should be set in the statusbar and then write it
8259 static void update_statusbar ( VikTrwLayer *vtl )
8261 // Get elevation data
8262 gdouble elev_gain, elev_loss;
8263 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8265 /* Find out actual distance of current track */
8266 gdouble distance = vik_track_get_length (vtl->current_track);
8268 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8272 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8274 /* if we haven't sync'ed yet, we don't have time to do more. */
8275 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8276 GList *iter = g_list_last ( vtl->current_track->trackpoints );
8277 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8279 static GdkPixmap *pixmap = NULL;
8281 // Need to check in case window has been resized
8282 w1 = vik_viewport_get_width(vvp);
8283 h1 = vik_viewport_get_height(vvp);
8285 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8287 gdk_drawable_get_size (pixmap, &w2, &h2);
8288 if (w1 != w2 || h1 != h2) {
8289 g_object_unref ( G_OBJECT ( pixmap ) );
8290 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8293 // Reset to background
8294 gdk_draw_drawable (pixmap,
8295 vtl->current_track_newpoint_gc,
8296 vik_viewport_get_pixmap(vvp),
8297 0, 0, 0, 0, -1, -1);
8299 draw_sync_t *passalong;
8302 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8304 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8305 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8306 // thus when we come to reset to the background it would include what we have already drawn!!
8307 gdk_draw_line ( pixmap,
8308 vtl->current_track_newpoint_gc,
8309 x1, y1, event->x, event->y );
8310 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8312 /* Find out actual distance of current track */
8313 gdouble distance = vik_track_get_length (vtl->current_track);
8315 // Now add distance to where the pointer is //
8318 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8319 vik_coord_to_latlon ( &coord, &ll );
8320 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8321 distance = distance + last_step;
8323 // Get elevation data
8324 gdouble elev_gain, elev_loss;
8325 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8327 // Adjust elevation data (if available) for the current pointer position
8329 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8330 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8331 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8332 // Adjust elevation of last track point
8333 if ( elev_new > last_tpt->altitude )
8335 elev_gain += elev_new - last_tpt->altitude;
8338 elev_loss += last_tpt->altitude - elev_new;
8343 // Display of the distance 'tooltip' during track creation is controlled by a preference
8345 if ( a_vik_get_create_track_tooltip() ) {
8347 gchar *str = distance_string (distance);
8349 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8350 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8351 pango_layout_set_text (pl, str, -1);
8353 pango_layout_get_pixel_size ( pl, &wd, &hd );
8356 // offset from cursor a bit depending on font size
8360 // Create a background block to make the text easier to read over the background map
8361 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8362 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8363 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8365 g_object_unref ( G_OBJECT ( pl ) );
8366 g_object_unref ( G_OBJECT ( background_block_gc ) );
8370 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
8371 passalong->vtl = vtl;
8372 passalong->pixmap = pixmap;
8373 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
8374 passalong->gc = vtl->current_track_newpoint_gc;
8378 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
8380 // Update statusbar with full gain/loss information
8381 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
8383 // draw pixmap when we have time to
8384 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
8385 vtl->draw_sync_done = FALSE;
8386 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
8388 return VIK_LAYER_TOOL_ACK;
8391 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
8393 if ( vtl->current_track && event->keyval == GDK_Escape ) {
8394 vtl->current_track = NULL;
8395 vik_layer_emit_update ( VIK_LAYER(vtl) );
8397 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
8399 if ( vtl->current_track->trackpoints )
8401 GList *last = g_list_last(vtl->current_track->trackpoints);
8402 g_free ( last->data );
8403 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8406 update_statusbar ( vtl );
8408 vik_layer_emit_update ( VIK_LAYER(vtl) );
8415 * Common function to handle trackpoint button requests on either a route or a track
8416 * . enables adding a point via normal click
8417 * . enables removal of last point via right click
8418 * . finishing of the track or route via double clicking
8420 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8424 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8427 if ( event->button == 2 ) {
8428 // As the display is panning, the new track pixmap is now invalid so don't draw it
8429 // otherwise this drawing done results in flickering back to an old image
8430 vtl->draw_sync_do = FALSE;
8434 if ( event->button == 3 )
8436 if ( !vtl->current_track )
8439 if ( vtl->current_track->trackpoints )
8441 GList *last = g_list_last(vtl->current_track->trackpoints);
8442 g_free ( last->data );
8443 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8445 vik_track_calculate_bounds ( vtl->current_track );
8446 update_statusbar ( vtl );
8448 vik_layer_emit_update ( VIK_LAYER(vtl) );
8452 if ( event->type == GDK_2BUTTON_PRESS )
8454 /* subtract last (duplicate from double click) tp then end */
8455 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8457 GList *last = g_list_last(vtl->current_track->trackpoints);
8458 g_free ( last->data );
8459 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8460 /* undo last, then end */
8461 vtl->current_track = NULL;
8463 vik_layer_emit_update ( VIK_LAYER(vtl) );
8467 tp = vik_trackpoint_new();
8468 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8470 /* snap to other TP */
8471 if ( event->state & GDK_CONTROL_MASK )
8473 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8475 tp->coord = other_tp->coord;
8478 tp->newsegment = FALSE;
8479 tp->has_timestamp = FALSE;
8482 if ( vtl->current_track ) {
8483 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
8484 /* Auto attempt to get elevation from DEM data (if it's available) */
8485 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8488 vtl->ct_x1 = vtl->ct_x2;
8489 vtl->ct_y1 = vtl->ct_y2;
8490 vtl->ct_x2 = event->x;
8491 vtl->ct_y2 = event->y;
8493 vik_layer_emit_update ( VIK_LAYER(vtl) );
8497 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8499 // ----------------------------------------------------- if current is a route - switch to new track
8500 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8502 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8503 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8505 new_track_create_common ( vtl, name );
8511 return tool_new_track_or_route_click ( vtl, event, vvp );
8514 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8516 if ( event->button == 2 ) {
8517 // Pan moving ended - enable potential point drawing again
8518 vtl->draw_sync_do = TRUE;
8519 vtl->draw_sync_done = TRUE;
8523 /*** New route ****/
8525 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8530 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8532 // -------------------------- if current is a track - switch to new route
8533 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8535 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8536 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) ) {
8537 new_route_create_common ( vtl, name );
8543 return tool_new_track_or_route_click ( vtl, event, vvp );
8546 /*** New waypoint ****/
8548 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8553 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8556 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8558 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8559 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8560 trw_layer_calculate_bounds_waypoints ( vtl );
8561 vik_layer_emit_update ( VIK_LAYER(vtl) );
8567 /*** Edit trackpoint ****/
8569 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8571 tool_ed_t *t = g_new(tool_ed_t, 1);
8577 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
8582 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8584 tool_ed_t *t = data;
8585 VikViewport *vvp = t->vvp;
8586 TPSearchParams params;
8587 /* OUTDATED DOCUMENTATION:
8588 find 5 pixel range on each side. then put these UTM, and a pointer
8589 to the winning track name (and maybe the winning track itself), and a
8590 pointer to the winning trackpoint, inside an array or struct. pass
8591 this along, do a foreach on the tracks which will do a foreach on the
8594 params.x = event->x;
8595 params.y = event->y;
8596 params.closest_track_id = NULL;
8597 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8598 params.closest_tp = NULL;
8599 params.closest_tpl = NULL;
8600 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8602 if ( event->button != 1 )
8605 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8608 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8611 if ( vtl->current_tpl )
8613 /* 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.) */
8614 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8615 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8620 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8622 if ( current_tr->visible &&
8623 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8624 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8625 marker_begin_move ( t, event->x, event->y );
8631 if ( vtl->tracks_visible )
8632 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8634 if ( params.closest_tp )
8636 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8637 vtl->current_tpl = params.closest_tpl;
8638 vtl->current_tp_id = params.closest_track_id;
8639 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8640 trw_layer_tpwin_init ( vtl );
8641 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8642 vik_layer_emit_update ( VIK_LAYER(vtl) );
8646 if ( vtl->routes_visible )
8647 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
8649 if ( params.closest_tp )
8651 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8652 vtl->current_tpl = params.closest_tpl;
8653 vtl->current_tp_id = params.closest_track_id;
8654 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8655 trw_layer_tpwin_init ( vtl );
8656 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8657 vik_layer_emit_update ( VIK_LAYER(vtl) );
8661 /* these aren't the droids you're looking for */
8665 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8667 tool_ed_t *t = data;
8668 VikViewport *vvp = t->vvp;
8670 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8676 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8679 if ( event->state & GDK_CONTROL_MASK )
8681 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8682 if ( tp && tp != vtl->current_tpl->data )
8683 new_coord = tp->coord;
8685 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8688 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8689 marker_moveto ( t, x, y );
8697 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8699 tool_ed_t *t = data;
8700 VikViewport *vvp = t->vvp;
8702 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8704 if ( event->button != 1)
8709 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8712 if ( event->state & GDK_CONTROL_MASK )
8714 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8715 if ( tp && tp != vtl->current_tpl->data )
8716 new_coord = tp->coord;
8719 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8720 if ( vtl->current_tp_track )
8721 vik_track_calculate_bounds ( vtl->current_tp_track );
8723 marker_end_move ( t );
8725 /* diff dist is diff from orig */
8727 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8729 vik_layer_emit_update ( VIK_LAYER(vtl) );
8736 /*** Route Finder ***/
8737 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8742 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8745 if ( !vtl ) return FALSE;
8746 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8747 if ( event->button == 3 && vtl->route_finder_current_track ) {
8749 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8751 vtl->route_finder_coord = *new_end;
8753 vik_layer_emit_update ( VIK_LAYER(vtl) );
8754 /* remove last ' to:...' */
8755 if ( vtl->route_finder_current_track->comment ) {
8756 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8757 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8758 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8759 last_to - vtl->route_finder_current_track->comment - 1);
8760 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8765 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8766 struct LatLon start, end;
8768 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8769 vik_coord_to_latlon ( &(tmp), &end );
8770 vtl->route_finder_coord = tmp; /* for continuations */
8772 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8773 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8774 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
8776 vtl->route_finder_check_added_track = TRUE;
8777 vtl->route_finder_started = FALSE;
8780 vik_routing_default_find ( vtl, start, end);
8782 /* see if anything was done -- a track was added or appended to */
8783 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8784 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 ) );
8785 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8786 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8787 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8788 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8791 if ( vtl->route_finder_added_track )
8792 vik_track_calculate_bounds ( vtl->route_finder_added_track );
8794 vtl->route_finder_added_track = NULL;
8795 vtl->route_finder_check_added_track = FALSE;
8796 vtl->route_finder_append = FALSE;
8798 vik_layer_emit_update ( VIK_LAYER(vtl) );
8800 vtl->route_finder_started = TRUE;
8801 vtl->route_finder_coord = tmp;
8802 vtl->route_finder_current_track = NULL;
8807 /*** Show picture ****/
8809 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8814 /* Params are: vvp, event, last match found or NULL */
8815 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8817 if ( wp->image && wp->visible )
8819 gint x, y, slackx, slacky;
8820 GdkEventButton *event = (GdkEventButton *) params[1];
8822 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8823 slackx = wp->image_width / 2;
8824 slacky = wp->image_height / 2;
8825 if ( x <= event->x + slackx && x >= event->x - slackx
8826 && y <= event->y + slacky && y >= event->y - slacky )
8828 params[2] = wp->image; /* we've found a match. however continue searching
8829 * since we want to find the last match -- that
8830 * is, the match that was drawn last. */
8835 static void trw_layer_show_picture ( gpointer pass_along[6] )
8837 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8839 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8842 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8843 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8844 g_free ( quoted_file );
8845 if ( ! g_spawn_command_line_async ( cmd, &err ) )
8847 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() );
8848 g_error_free ( err );
8851 #endif /* WINDOWS */
8854 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8856 gpointer params[3] = { vvp, event, NULL };
8857 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8859 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8862 static gpointer pass_along[6];
8863 pass_along[0] = vtl;
8864 pass_along[5] = params[2];
8865 trw_layer_show_picture ( pass_along );
8866 return TRUE; /* found a match */
8869 return FALSE; /* go through other layers, searching for a match */
8872 /***************************************************************************
8874 ***************************************************************************/
8877 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8879 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8880 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8883 /* Structure for thumbnail creating data used in the background thread */
8885 VikTrwLayer *vtl; // Layer needed for redrawing
8886 GSList *pics; // Image list
8887 } thumbnail_create_thread_data;
8889 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8891 guint total = g_slist_length(tctd->pics), done = 0;
8892 while ( tctd->pics )
8894 a_thumbnails_create ( (gchar *) tctd->pics->data );
8895 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8897 return -1; /* Abort thread */
8899 tctd->pics = tctd->pics->next;
8902 // Redraw to show the thumbnails as they are now created
8903 if ( IS_VIK_LAYER(tctd->vtl) )
8904 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8909 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8911 while ( tctd->pics )
8913 g_free ( tctd->pics->data );
8914 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8919 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8921 if ( ! vtl->has_verified_thumbnails )
8923 GSList *pics = NULL;
8924 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8927 gint len = g_slist_length ( pics );
8928 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8929 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8932 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8934 (vik_thr_func) create_thumbnails_thread,
8936 (vik_thr_free_func) thumbnail_create_thread_free,
8944 static const gchar* my_track_colors ( gint ii )
8946 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8958 // Fast and reliable way of returning a colour
8959 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8962 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8964 GHashTableIter iter;
8965 gpointer key, value;
8969 g_hash_table_iter_init ( &iter, vtl->tracks );
8971 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8973 // Tracks get a random spread of colours if not already assigned
8974 if ( ! VIK_TRACK(value)->has_color ) {
8975 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8976 VIK_TRACK(value)->color = vtl->track_color;
8978 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8980 VIK_TRACK(value)->has_color = TRUE;
8983 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8986 if (ii > VIK_TRW_LAYER_TRACK_GCS)
8992 g_hash_table_iter_init ( &iter, vtl->routes );
8994 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8996 // Routes get an intermix of reds
8997 if ( ! VIK_TRACK(value)->has_color ) {
8999 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9001 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9002 VIK_TRACK(value)->has_color = TRUE;
9005 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9012 * (Re)Calculate the bounds of the waypoints in this layer,
9013 * This should be called whenever waypoints are changed
9015 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9017 struct LatLon topleft = { 0.0, 0.0 };
9018 struct LatLon bottomright = { 0.0, 0.0 };
9021 GHashTableIter iter;
9022 gpointer key, value;
9024 g_hash_table_iter_init ( &iter, vtl->waypoints );
9026 // Set bounds to first point
9027 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9028 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9029 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9032 // Ensure there is another point...
9033 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9035 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9037 // See if this point increases the bounds.
9038 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9040 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9041 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9042 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9043 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9047 vtl->waypoints_bbox.north = topleft.lat;
9048 vtl->waypoints_bbox.east = bottomright.lon;
9049 vtl->waypoints_bbox.south = bottomright.lat;
9050 vtl->waypoints_bbox.west = topleft.lon;
9053 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9055 vik_track_calculate_bounds ( trk );
9058 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9060 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9061 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9064 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9066 if ( ! VIK_LAYER(vtl)->vt )
9069 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9070 if ( g_hash_table_size (vtl->tracks) > 1 )
9071 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9073 if ( g_hash_table_size (vtl->routes) > 1 )
9074 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9076 if ( g_hash_table_size (vtl->waypoints) > 1 )
9077 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9080 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9082 trw_layer_verify_thumbnails ( vtl, vvp );
9083 trw_layer_track_alloc_colors ( vtl );
9085 trw_layer_calculate_bounds_waypoints ( vtl );
9086 trw_layer_calculate_bounds_tracks ( vtl );
9088 // Apply treeview sort after loading all the tracks for this layer
9089 // (rather than sorted insert on each individual track additional)
9090 // and after subsequent changes to the properties as the specified order may have changed.
9091 // since the sorting of a treeview section is now very quick
9092 // NB sorting is also performed after every name change as well to maintain the list order
9093 trw_layer_sort_all ( vtl );
9096 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9098 return vtl->coord_mode;
9102 * Uniquify the whole layer
9103 * Also requires the layers panel as the names shown there need updating too
9104 * Returns whether the operation was successful or not
9106 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9109 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9110 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9111 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9117 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9119 vik_coord_convert ( &(wp->coord), *dest_mode );
9122 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9124 vik_track_convert ( tr, *dest_mode );
9127 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9129 if ( vtl->coord_mode != dest_mode )
9131 vtl->coord_mode = dest_mode;
9132 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9133 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9134 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9138 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9140 vtl->menu_selection = selection;
9143 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9145 return (vtl->menu_selection);
9148 /* ----------- Downloading maps along tracks --------------- */
9150 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9152 /* TODO: calculating based on current size of viewport */
9153 const gdouble w_at_zoom_0_125 = 0.0013;
9154 const gdouble h_at_zoom_0_125 = 0.0011;
9155 gdouble zoom_factor = zoom_level/0.125;
9157 wh->lat = h_at_zoom_0_125 * zoom_factor;
9158 wh->lon = w_at_zoom_0_125 * zoom_factor;
9160 return 0; /* all OK */
9163 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9165 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9166 (dist->lat >= ABS(to->north_south - from->north_south)))
9169 VikCoord *coord = g_malloc(sizeof(VikCoord));
9170 coord->mode = VIK_COORD_LATLON;
9172 if (ABS(gradient) < 1) {
9173 if (from->east_west > to->east_west)
9174 coord->east_west = from->east_west - dist->lon;
9176 coord->east_west = from->east_west + dist->lon;
9177 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9179 if (from->north_south > to->north_south)
9180 coord->north_south = from->north_south - dist->lat;
9182 coord->north_south = from->north_south + dist->lat;
9183 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9189 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9191 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9192 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9194 VikCoord *next = from;
9196 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9198 list = g_list_prepend(list, next);
9204 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9206 typedef struct _Rect {
9211 #define GLRECT(iter) ((Rect *)((iter)->data))
9214 GList *rects_to_download = NULL;
9217 if (get_download_area_width(vvp, zoom_level, &wh))
9220 GList *iter = tr->trackpoints;
9224 gboolean new_map = TRUE;
9225 VikCoord *cur_coord, tl, br;
9228 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9230 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9231 rect = g_malloc(sizeof(Rect));
9234 rect->center = *cur_coord;
9235 rects_to_download = g_list_prepend(rects_to_download, rect);
9240 gboolean found = FALSE;
9241 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9242 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9253 GList *fillins = NULL;
9254 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9255 /* seems that ATM the function get_next_coord works only for LATLON */
9256 if ( cur_coord->mode == VIK_COORD_LATLON ) {
9257 /* fill-ins for far apart points */
9258 GList *cur_rect, *next_rect;
9259 for (cur_rect = rects_to_download;
9260 (next_rect = cur_rect->next) != NULL;
9261 cur_rect = cur_rect->next) {
9262 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9263 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9264 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9268 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9271 GList *iter = fillins;
9273 cur_coord = (VikCoord *)(iter->data);
9274 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9275 rect = g_malloc(sizeof(Rect));
9278 rect->center = *cur_coord;
9279 rects_to_download = g_list_prepend(rects_to_download, rect);
9284 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9285 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9289 for (iter = fillins; iter; iter = iter->next)
9291 g_list_free(fillins);
9293 if (rects_to_download) {
9294 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9295 g_free(rect_iter->data);
9296 g_list_free(rects_to_download);
9300 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9303 gint selected_map, default_map;
9304 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9305 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9306 gint selected_zoom, default_zoom;
9310 VikTrwLayer *vtl = pass_along[0];
9311 VikLayersPanel *vlp = pass_along[1];
9313 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9314 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9316 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9320 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9322 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9323 int num_maps = g_list_length(vmls);
9326 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
9330 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9331 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9333 gchar **np = map_names;
9334 VikMapsLayer **lp = map_layers;
9335 for (i = 0; i < num_maps; i++) {
9336 gboolean dup = FALSE;
9337 vml = (VikMapsLayer *)(vmls->data);
9338 for (j = 0; j < i; j++) { /* no duplicate allowed */
9339 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
9346 *np++ = vik_maps_layer_get_map_label(vml);
9352 num_maps = lp - map_layers;
9354 for (default_map = 0; default_map < num_maps; default_map++) {
9355 /* TODO: check for parent layer's visibility */
9356 if (VIK_LAYER(map_layers[default_map])->visible)
9359 default_map = (default_map == num_maps) ? 0 : default_map;
9361 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9362 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
9363 if (cur_zoom == zoom_vals[default_zoom])
9366 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
9368 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
9371 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9374 for (i = 0; i < num_maps; i++)
9375 g_free(map_names[i]);
9383 /**** lowest waypoint number calculation ***/
9384 static gint highest_wp_number_name_to_number(const gchar *name) {
9385 if ( strlen(name) == 3 ) {
9387 if ( n < 100 && name[0] != '0' )
9389 if ( n < 10 && name[0] != '0' )
9397 static void highest_wp_number_reset(VikTrwLayer *vtl)
9399 vtl->highest_wp_number = -1;
9402 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
9404 /* if is bigger that top, add it */
9405 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
9406 if ( new_wp_num > vtl->highest_wp_number )
9407 vtl->highest_wp_number = new_wp_num;
9410 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
9412 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
9413 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
9414 if ( vtl->highest_wp_number == old_wp_num ) {
9416 vtl->highest_wp_number--;
9418 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9419 /* search down until we find something that *does* exist */
9421 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
9422 vtl->highest_wp_number--;
9423 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9428 /* get lowest unused number */
9429 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
9432 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
9434 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
9435 return g_strdup(buf);