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"
61 #include "vikrouting.h"
63 #include "icons/icons.h"
77 #include <gdk/gdkkeysyms.h>
79 #include <glib/gstdio.h>
80 #include <glib/gi18n.h>
82 #define VIK_TRW_LAYER_TRACK_GC 6
83 #define VIK_TRW_LAYER_TRACK_GCS 10
84 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
85 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
86 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
87 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
88 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
89 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
91 #define DRAWMODE_BY_TRACK 0
92 #define DRAWMODE_BY_SPEED 1
93 #define DRAWMODE_ALL_SAME_COLOR 2
94 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
95 // as we are (re)calculating the colour for every point
100 /* this is how it knows when you click if you are clicking close to a trackpoint. */
101 #define TRACKPOINT_SIZE_APPROX 5
102 #define WAYPOINT_SIZE_APPROX 5
104 #define MIN_STOP_LENGTH 15
105 #define MAX_STOP_LENGTH 86400
106 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
107 /* this is multiplied by user-inputted value from 1-100. */
109 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
111 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
113 FS_XX_SMALL = 0, // 'xx-small'
116 FS_MEDIUM, // DEFAULT
123 struct _VikTrwLayer {
126 GHashTable *tracks_iters;
128 GHashTable *routes_iters;
129 GHashTable *waypoints_iters;
130 GHashTable *waypoints;
131 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
132 gboolean tracks_visible, routes_visible, waypoints_visible;
133 LatLonBBox waypoints_bbox;
135 gboolean track_draw_labels;
138 guint8 drawpoints_size;
139 guint8 drawelevation;
140 guint8 elevation_factor;
144 guint8 drawdirections;
145 guint8 drawdirections_size;
146 guint8 line_thickness;
147 guint8 bg_line_thickness;
148 vik_layer_sort_order_t track_sort_order;
150 PangoLayout *tracklabellayout;
151 font_size_t track_font_size;
152 gchar *track_fsize_str;
156 gboolean wp_draw_symbols;
157 font_size_t wp_font_size;
159 vik_layer_sort_order_t wp_sort_order;
161 gdouble track_draw_speed_factor;
163 GdkGC *track_1color_gc;
164 GdkColor track_color;
165 GdkGC *current_track_gc;
166 // Separate GC for a track's potential new point as drawn via separate method
167 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
168 GdkGC *current_track_newpoint_gc;
169 GdkGC *track_bg_gc; GdkColor track_bg_color;
170 GdkGC *waypoint_gc; GdkColor waypoint_color;
171 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
172 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
174 GdkFont *waypoint_font;
175 VikTrack *current_track; // ATM shared between new tracks and new routes
176 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
177 gboolean draw_sync_done;
178 gboolean draw_sync_do;
180 VikCoordMode coord_mode;
182 /* wp editing tool */
183 VikWaypoint *current_wp;
184 gpointer current_wp_id;
186 gboolean waypoint_rightclick;
188 /* track editing tool */
190 VikTrack *current_tp_track;
191 gpointer current_tp_id;
192 VikTrwLayerTpwin *tpwin;
194 /* track editing tool -- more specifically, moving tps */
197 /* route finder tool */
198 gboolean route_finder_started;
199 VikCoord route_finder_coord;
200 gboolean route_finder_check_added_track;
201 VikTrack *route_finder_added_track;
202 VikTrack *route_finder_current_track;
203 gboolean route_finder_append;
210 guint16 image_cache_size;
212 /* for waypoint text */
213 PangoLayout *wplabellayout;
215 gboolean has_verified_thumbnails;
217 GtkMenu *wp_right_click_menu;
218 GtkMenu *track_right_click_menu;
221 VikStdLayerMenuItem menu_selection;
223 gint highest_wp_number;
226 GtkWidget *tracks_analysis_dialog;
229 /* A caached waypoint image. */
232 gchar *image; /* filename */
235 struct DrawingParams {
240 guint16 width, height;
241 gdouble cc; // Cosine factor in track directions
242 gdouble ss; // Sine factor in track directions
243 const VikCoord *center;
244 gboolean one_zone, lat_lon;
245 gdouble ce1, ce2, cn1, cn2;
249 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
251 static void trw_layer_delete_item ( gpointer pass_along[6] );
252 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
253 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
255 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
256 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
258 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
259 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
261 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
262 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
264 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl );
266 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
267 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
268 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
269 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
270 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
271 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
272 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
273 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
274 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
275 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
276 static void trw_layer_append_track ( gpointer pass_along[6] );
277 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
278 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
279 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
280 static void trw_layer_split_segments ( gpointer pass_along[6] );
281 static void trw_layer_delete_point_selected ( gpointer pass_along[6] );
282 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
283 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
284 static void trw_layer_reverse ( gpointer pass_along[6] );
285 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
286 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
287 static void trw_layer_show_picture ( gpointer pass_along[6] );
288 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
290 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
291 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
292 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
293 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
294 static void trw_layer_new_wp ( gpointer lav[2] );
295 static void trw_layer_new_track ( gpointer lav[2] );
296 static void trw_layer_new_route ( gpointer lav[2] );
297 static void trw_layer_finish_track ( gpointer lav[2] );
298 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
299 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
300 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
301 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
302 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
303 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
304 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
305 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
306 #ifdef VIK_CONFIG_GEOTAG
307 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
308 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
309 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
310 static void trw_layer_geotagging ( gpointer lav[2] );
312 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
313 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
314 #ifdef VIK_CONFIG_OPENSTREETMAP
315 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
316 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
318 #ifdef VIK_CONFIG_GEOCACHES
319 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
321 #ifdef VIK_CONFIG_GEOTAG
322 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
324 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
325 static void trw_layer_gps_upload ( gpointer lav[2] );
327 // Specific route versions:
328 // Most track handling functions can handle operating on the route list
329 // However these ones are easier in separate functions
330 static void trw_layer_auto_routes_view ( gpointer lav[2] );
331 static void trw_layer_delete_all_routes ( gpointer lav[2] );
332 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
335 static void trw_layer_properties_item ( gpointer pass_along[7] );
336 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
337 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
338 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
340 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
341 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
342 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
344 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
345 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
346 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
347 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
349 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
350 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
351 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
352 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
353 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
354 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
355 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
356 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
357 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
358 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
359 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
360 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
361 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
362 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
363 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
364 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
365 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
366 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
367 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
368 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
369 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
370 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
371 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
373 static void cached_pixbuf_free ( CachedPixbuf *cp );
374 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
376 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
377 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
379 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
380 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
382 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
383 static void highest_wp_number_reset(VikTrwLayer *vtl);
384 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
385 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
387 // Note for the following tool GtkRadioActionEntry texts:
388 // the very first text value is an internal name not displayed anywhere
389 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
390 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
391 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
392 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
393 static VikToolInterface trw_layer_tools[] = {
394 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
395 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
396 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
398 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
400 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
401 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
402 (VikToolMouseFunc) tool_new_track_click,
403 (VikToolMouseMoveFunc) tool_new_track_move,
404 (VikToolMouseFunc) tool_new_track_release,
405 (VikToolKeyFunc) tool_new_track_key_press,
406 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
407 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
409 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
410 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
411 (VikToolMouseFunc) tool_new_route_click,
412 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
413 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
414 (VikToolKeyFunc) tool_new_track_key_press, // -/#
415 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
416 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
418 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
419 (VikToolConstructorFunc) tool_edit_waypoint_create,
420 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
422 (VikToolMouseFunc) tool_edit_waypoint_click,
423 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
424 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
426 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
428 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
429 (VikToolConstructorFunc) tool_edit_trackpoint_create,
430 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
432 (VikToolMouseFunc) tool_edit_trackpoint_click,
433 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
434 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
436 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
438 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
439 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
440 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
442 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
444 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
445 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
446 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
448 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
452 TOOL_CREATE_WAYPOINT=0,
456 TOOL_EDIT_TRACKPOINT,
462 /****** PARAMETERS ******/
464 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
465 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
467 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
468 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
470 #define MIN_POINT_SIZE 2
471 #define MAX_POINT_SIZE 10
473 #define MIN_ARROW_SIZE 3
474 #define MAX_ARROW_SIZE 20
476 static VikLayerParamScale params_scales[] = {
477 /* min max step digits */
478 { 1, 10, 1, 0 }, /* line_thickness */
479 { 0, 100, 1, 0 }, /* track draw speed factor */
480 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
481 /* 5 * step == how much to turn */
482 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
483 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
484 { 5, 500, 5, 0 }, // 5: image cache_size - " "
485 { 0, 8, 1, 0 }, // 6: Background line thickness
486 { 1, 64, 1, 0 }, /* wpsize */
487 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
488 { 1, 100, 1, 0 }, // 9: elevation factor
489 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
490 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
493 static gchar* params_font_sizes[] = {
494 N_("Extra Extra Small"),
500 N_("Extra Extra Large"),
503 // Needs to align with vik_layer_sort_order_t
504 static gchar* params_sort_order[] = {
506 N_("Name Ascending"),
507 N_("Name Descending"),
511 static VikLayerParamData black_color_default ( void ) {
512 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
514 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
515 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
516 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
517 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
518 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
519 static VikLayerParamData trackbgcolor_default ( void ) {
520 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
522 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
523 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
524 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
526 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
527 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
528 static VikLayerParamData wptextcolor_default ( void ) {
529 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
531 static VikLayerParamData wpbgcolor_default ( void ) {
532 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
534 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
535 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
537 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
538 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
539 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
541 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
543 VikLayerParam trw_layer_params[] = {
544 { 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 },
545 { 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 },
546 { 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 },
548 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
549 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
550 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
551 { 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 },
552 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
553 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
554 { 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 },
555 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
556 { 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 },
557 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
558 { 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 },
559 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
560 { 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 },
561 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
562 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
563 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 },
564 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
566 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶ms_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
567 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
568 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, ¶ms_scales[1], NULL,
569 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
570 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
572 { 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 },
573 { 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 },
574 { 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 },
575 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
576 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
577 { 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 },
578 { 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 },
579 { 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 },
580 { 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 },
581 { 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 },
583 { 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 },
584 { 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 },
585 { 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 },
586 { 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 },
589 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
591 // Sublayer visibilities
634 *** 1) Add to trw_layer_params and enumeration
635 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
638 /****** END PARAMETERS ******/
640 /* Layer Interface function definitions */
641 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
642 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
643 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
644 static void trw_layer_free ( VikTrwLayer *trwlayer );
645 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
646 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
647 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
648 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
649 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
650 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
651 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
652 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
653 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
654 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
655 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
656 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
657 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
658 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
659 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
660 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
661 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
662 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
663 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
664 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
665 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
666 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
667 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
668 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
669 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
670 /* End Layer Interface function definitions */
672 VikLayerInterface vik_trw_layer_interface = {
679 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
683 params_groups, /* params_groups */
684 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
688 (VikLayerFuncCreate) trw_layer_create,
689 (VikLayerFuncRealize) trw_layer_realize,
690 (VikLayerFuncPostRead) trw_layer_post_read,
691 (VikLayerFuncFree) trw_layer_free,
693 (VikLayerFuncProperties) NULL,
694 (VikLayerFuncDraw) trw_layer_draw,
695 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
697 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
698 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
700 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
701 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
703 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
704 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
705 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
706 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
707 (VikLayerFuncLayerSelected) trw_layer_selected,
709 (VikLayerFuncMarshall) trw_layer_marshall,
710 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
712 (VikLayerFuncSetParam) trw_layer_set_param,
713 (VikLayerFuncGetParam) trw_layer_get_param,
715 (VikLayerFuncReadFileData) a_gpspoint_read_file,
716 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
718 (VikLayerFuncDeleteItem) trw_layer_del_item,
719 (VikLayerFuncCutItem) trw_layer_cut_item,
720 (VikLayerFuncCopyItem) trw_layer_copy_item,
721 (VikLayerFuncPasteItem) trw_layer_paste_item,
722 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
724 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
726 (VikLayerFuncSelectClick) trw_layer_select_click,
727 (VikLayerFuncSelectMove) trw_layer_select_move,
728 (VikLayerFuncSelectRelease) trw_layer_select_release,
729 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
732 GType vik_trw_layer_get_type ()
734 static GType vtl_type = 0;
738 static const GTypeInfo vtl_info =
740 sizeof (VikTrwLayerClass),
741 NULL, /* base_init */
742 NULL, /* base_finalize */
743 NULL, /* class init */
744 NULL, /* class_finalize */
745 NULL, /* class_data */
746 sizeof (VikTrwLayer),
748 NULL /* instance init */
750 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
756 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
758 static gpointer pass_along[6];
764 pass_along[1] = NULL;
765 pass_along[2] = GINT_TO_POINTER (subtype);
766 pass_along[3] = sublayer;
767 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
768 pass_along[5] = NULL;
770 trw_layer_delete_item ( pass_along );
773 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
775 static gpointer pass_along[6];
781 pass_along[1] = NULL;
782 pass_along[2] = GINT_TO_POINTER (subtype);
783 pass_along[3] = sublayer;
784 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
785 pass_along[5] = NULL;
787 trw_layer_copy_item_cb(pass_along);
788 trw_layer_cut_item_cb(pass_along);
791 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
793 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
794 gint subtype = GPOINTER_TO_INT (pass_along[2]);
795 gpointer * sublayer = pass_along[3];
799 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
803 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
804 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
805 if ( wp && wp->name )
808 name = NULL; // Broken :(
810 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
811 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
812 if ( trk && trk->name )
815 name = NULL; // Broken :(
818 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
819 if ( trk && trk->name )
822 name = NULL; // Broken :(
825 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
826 subtype, len, name, data);
830 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
832 trw_layer_copy_item_cb(pass_along);
833 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
834 trw_layer_delete_item(pass_along);
837 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
839 // Slightly cheating method, routing via the panels capability
840 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
843 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
853 GByteArray *ba = g_byte_array_new ();
855 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
856 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
857 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
858 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
860 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
863 g_byte_array_append ( ba, id, il );
871 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
878 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
882 w = vik_waypoint_unmarshall ( item, len );
883 // When copying - we'll create a new name based on the original
884 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
885 vik_trw_layer_add_waypoint ( vtl, name, w );
886 waypoint_convert (NULL, w, &vtl->coord_mode);
889 trw_layer_calculate_bounds_waypoints ( vtl );
891 // Consider if redraw necessary for the new item
892 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
893 vik_layer_emit_update ( VIK_LAYER(vtl) );
896 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
900 t = vik_track_unmarshall ( item, len );
901 // When copying - we'll create a new name based on the original
902 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
903 vik_trw_layer_add_track ( vtl, name, t );
904 vik_track_convert (t, vtl->coord_mode);
907 // Consider if redraw necessary for the new item
908 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
909 vik_layer_emit_update ( VIK_LAYER(vtl) );
912 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
916 t = vik_track_unmarshall ( item, len );
917 // When copying - we'll create a new name based on the original
918 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
919 vik_trw_layer_add_route ( vtl, name, t );
920 vik_track_convert (t, vtl->coord_mode);
923 // Consider if redraw necessary for the new item
924 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
925 vik_layer_emit_update ( VIK_LAYER(vtl) );
931 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
938 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
942 case PARAM_TV: vtl->tracks_visible = data.b; break;
943 case PARAM_WV: vtl->waypoints_visible = data.b; break;
944 case PARAM_RV: vtl->routes_visible = data.b; break;
945 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
946 case PARAM_TLFONTSIZE:
947 if ( data.u < FS_NUM_SIZES ) {
948 vtl->track_font_size = data.u;
949 g_free ( vtl->track_fsize_str );
950 switch ( vtl->track_font_size ) {
951 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
952 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
953 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
954 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
955 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
956 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
957 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
961 case PARAM_DM: vtl->drawmode = data.u; break;
963 vtl->track_color = data.c;
964 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
966 case PARAM_DP: vtl->drawpoints = data.b; break;
968 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
969 vtl->drawpoints_size = data.u;
971 case PARAM_DE: vtl->drawelevation = data.b; break;
972 case PARAM_DS: vtl->drawstops = data.b; break;
973 case PARAM_DL: vtl->drawlines = data.b; break;
974 case PARAM_DD: vtl->drawdirections = data.b; break;
976 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
977 vtl->drawdirections_size = data.u;
979 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
980 vtl->stop_length = data.u;
982 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
983 vtl->elevation_factor = data.u;
985 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
987 vtl->line_thickness = data.u;
988 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
991 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
993 vtl->bg_line_thickness = data.u;
994 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
998 vtl->track_bg_color = data.c;
999 if ( vtl->track_bg_gc )
1000 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1002 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1003 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1004 case PARAM_DLA: vtl->drawlabels = data.b; break;
1005 case PARAM_DI: vtl->drawimages = data.b; break;
1006 case PARAM_IS: if ( data.u != vtl->image_size )
1008 vtl->image_size = data.u;
1009 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1010 g_queue_free ( vtl->image_cache );
1011 vtl->image_cache = g_queue_new ();
1014 case PARAM_IA: vtl->image_alpha = data.u; break;
1015 case PARAM_ICS: vtl->image_cache_size = data.u;
1016 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1017 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1020 vtl->waypoint_color = data.c;
1021 if ( vtl->waypoint_gc )
1022 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1025 vtl->waypoint_text_color = data.c;
1026 if ( vtl->waypoint_text_gc )
1027 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1030 vtl->waypoint_bg_color = data.c;
1031 if ( vtl->waypoint_bg_gc )
1032 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1035 vtl->wpbgand = data.b;
1036 if ( vtl->waypoint_bg_gc )
1037 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1039 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1040 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1041 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1042 case PARAM_WPFONTSIZE:
1043 if ( data.u < FS_NUM_SIZES ) {
1044 vtl->wp_font_size = data.u;
1045 g_free ( vtl->wp_fsize_str );
1046 switch ( vtl->wp_font_size ) {
1047 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1048 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1049 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1050 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1051 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1052 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1053 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1057 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1062 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1064 VikLayerParamData rv;
1067 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1068 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1069 case PARAM_RV: rv.b = vtl->routes_visible; break;
1070 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1071 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1072 case PARAM_DM: rv.u = vtl->drawmode; break;
1073 case PARAM_TC: rv.c = vtl->track_color; break;
1074 case PARAM_DP: rv.b = vtl->drawpoints; break;
1075 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1076 case PARAM_DE: rv.b = vtl->drawelevation; break;
1077 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1078 case PARAM_DS: rv.b = vtl->drawstops; break;
1079 case PARAM_SL: rv.u = vtl->stop_length; break;
1080 case PARAM_DL: rv.b = vtl->drawlines; break;
1081 case PARAM_DD: rv.b = vtl->drawdirections; break;
1082 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1083 case PARAM_LT: rv.u = vtl->line_thickness; break;
1084 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1085 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1086 case PARAM_DI: rv.b = vtl->drawimages; break;
1087 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1088 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1089 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1090 case PARAM_IS: rv.u = vtl->image_size; break;
1091 case PARAM_IA: rv.u = vtl->image_alpha; break;
1092 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1093 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1094 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1095 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1096 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1097 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1098 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1099 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1100 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1101 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1106 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1113 // Use byte arrays to store sublayer data
1114 // much like done elsewhere e.g. vik_layer_marshall_params()
1115 GByteArray *ba = g_byte_array_new ( );
1120 guint object_length;
1123 // the length of the item
1124 // the sublayer type of item
1125 // the the actual item
1126 #define tlm_append(object_pointer, size, type) \
1128 object_length = (size); \
1129 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1130 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1131 g_byte_array_append ( ba, (object_pointer), object_length );
1133 // Layer parameters first
1134 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1135 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1136 g_byte_array_append ( ba, pd, pl );
1139 // Now sublayer data
1140 GHashTableIter iter;
1141 gpointer key, value;
1144 g_hash_table_iter_init ( &iter, vtl->waypoints );
1145 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1146 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1147 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1152 g_hash_table_iter_init ( &iter, vtl->tracks );
1153 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1154 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1155 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1160 g_hash_table_iter_init ( &iter, vtl->routes );
1161 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1162 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1163 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1173 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1175 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1177 gint consumed_length;
1179 // First the overall layer parameters
1180 memcpy(&pl, data, sizeof(pl));
1182 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1185 consumed_length = pl;
1186 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1188 #define tlm_size (*(gint *)data)
1189 // See marshalling above for order of how this is written
1191 data += sizeof_len_and_subtype + tlm_size;
1193 // Now the individual sublayers:
1195 while ( *data && consumed_length < len ) {
1196 // Normally four extra bytes at the end of the datastream
1197 // (since it's a GByteArray and that's where it's length is stored)
1198 // So only attempt read when there's an actual block of sublayer data
1199 if ( consumed_length + tlm_size < len ) {
1201 // Reuse pl to read the subtype from the data stream
1202 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1204 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1205 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1206 gchar *name = g_strdup ( trk->name );
1207 vik_trw_layer_add_track ( vtl, name, trk );
1210 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1211 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1212 gchar *name = g_strdup ( wp->name );
1213 vik_trw_layer_add_waypoint ( vtl, name, wp );
1216 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1217 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1218 gchar *name = g_strdup ( trk->name );
1219 vik_trw_layer_add_route ( vtl, name, trk );
1223 consumed_length += tlm_size + sizeof_len_and_subtype;
1226 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1228 // Not stored anywhere else so need to regenerate
1229 trw_layer_calculate_bounds_waypoints ( vtl );
1234 // Keep interesting hash function at least visible
1236 static guint strcase_hash(gconstpointer v)
1238 // 31 bit hash function
1241 gchar s[128]; // malloc is too slow for reading big files
1244 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1245 p[i] = toupper(t[i]);
1251 for (p += 1; *p != '\0'; p++)
1252 h = (h << 5) - h + *p;
1259 // Stick a 1 at the end of the function name to make it more unique
1260 // thus more easily searchable in a simple text editor
1261 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1263 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1264 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1266 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1267 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1269 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1270 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1271 // and with normal PC processing capabilities - it has negligibile performance impact
1272 // This also minimized the amount of rework - as the management of the hash tables already exists.
1274 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1275 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1276 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1278 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1279 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1280 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1281 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1282 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1283 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1285 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1287 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1289 // Param settings that are not available via the GUI
1290 // Force to on after processing params (which defaults them to off with a zero value)
1291 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1293 rv->draw_sync_done = TRUE;
1294 rv->draw_sync_do = TRUE;
1295 // Everything else is 0, FALSE or NULL
1301 static void trw_layer_free ( VikTrwLayer *trwlayer )
1303 g_hash_table_destroy(trwlayer->waypoints);
1304 g_hash_table_destroy(trwlayer->waypoints_iters);
1305 g_hash_table_destroy(trwlayer->tracks);
1306 g_hash_table_destroy(trwlayer->tracks_iters);
1307 g_hash_table_destroy(trwlayer->routes);
1308 g_hash_table_destroy(trwlayer->routes_iters);
1310 /* ODC: replace with GArray */
1311 trw_layer_free_track_gcs ( trwlayer );
1313 if ( trwlayer->wp_right_click_menu )
1314 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1316 if ( trwlayer->track_right_click_menu )
1317 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1319 if ( trwlayer->tracklabellayout != NULL)
1320 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1322 if ( trwlayer->wplabellayout != NULL)
1323 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1325 if ( trwlayer->waypoint_gc != NULL )
1326 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1328 if ( trwlayer->waypoint_text_gc != NULL )
1329 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1331 if ( trwlayer->waypoint_bg_gc != NULL )
1332 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1334 g_free ( trwlayer->wp_fsize_str );
1335 g_free ( trwlayer->track_fsize_str );
1337 if ( trwlayer->tpwin != NULL )
1338 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1340 if ( trwlayer->tracks_analysis_dialog != NULL )
1341 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1343 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1344 g_queue_free ( trwlayer->image_cache );
1347 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1351 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1352 dp->xmpp = vik_viewport_get_xmpp ( vp );
1353 dp->ympp = vik_viewport_get_ympp ( vp );
1354 dp->width = vik_viewport_get_width ( vp );
1355 dp->height = vik_viewport_get_height ( vp );
1356 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1357 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1359 dp->center = vik_viewport_get_center ( vp );
1360 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1361 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1366 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1367 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1368 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1370 dp->ce1 = dp->center->east_west-w2;
1371 dp->ce2 = dp->center->east_west+w2;
1372 dp->cn1 = dp->center->north_south-h2;
1373 dp->cn2 = dp->center->north_south+h2;
1374 } else if ( dp->lat_lon ) {
1375 VikCoord upperleft, bottomright;
1376 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1377 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1378 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1379 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1380 dp->ce1 = upperleft.east_west;
1381 dp->ce2 = bottomright.east_west;
1382 dp->cn1 = bottomright.north_south;
1383 dp->cn2 = upperleft.north_south;
1386 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1390 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1391 * Here a simple traffic like light colour system is used:
1392 * . slow points are red
1393 * . average is yellow
1394 * . fast points are green
1396 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1399 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1400 if ( average_speed > 0 ) {
1401 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1402 if ( rv < low_speed )
1403 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1404 else if ( rv > high_speed )
1405 return VIK_TRW_LAYER_TRACK_GC_FAST;
1407 return VIK_TRW_LAYER_TRACK_GC_AVER;
1410 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1413 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1415 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1416 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1417 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1418 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1422 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1424 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1426 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1427 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1429 // Fallback if parse failure
1430 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1432 g_free ( label_markup );
1434 gint label_x, label_y;
1436 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1438 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1439 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1443 * distance_in_preferred_units:
1444 * @dist: The source distance in standard SI Units (i.e. metres)
1446 * TODO: This is a generic function that could be moved into globals.c or utils.c
1448 * Probably best used if you have a only few conversions to perform.
1449 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1450 * since it will be doing the preference check on each call
1452 * Returns: The distance in the units as specified by the preferences
1454 static gdouble distance_in_preferred_units ( gdouble dist )
1457 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1458 switch (dist_units) {
1459 case VIK_UNITS_DISTANCE_MILES:
1460 mydist = VIK_METERS_TO_MILES(dist);
1462 // VIK_UNITS_DISTANCE_KILOMETRES:
1464 mydist = dist/1000.0;
1471 * trw_layer_draw_dist_labels:
1473 * Draw a few labels along a track at nicely seperated distances
1474 * This might slow things down if there's many tracks being displayed with this on.
1476 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1478 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1479 25.0, 40.0, 50.0, 75.0, 100.0,
1480 150.0, 200.0, 250.0, 500.0, 1000.0};
1482 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1484 // Convert to specified unit to find the friendly breakdown value
1485 dist = distance_in_preferred_units ( dist );
1489 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1490 if ( chunksd[i] > dist ) {
1492 dist = chunksd[index];
1497 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1499 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1500 gdouble dist_i = dist * i;
1502 // Convert distance back into metres for use in finding a trackpoint
1503 switch (dist_units) {
1504 case VIK_UNITS_DISTANCE_MILES:
1505 dist_i = VIK_MILES_TO_METERS(dist_i);
1507 // VIK_UNITS_DISTANCE_KILOMETRES:
1509 dist_i = dist_i*1000.0;
1513 gdouble dist_current = 0.0;
1514 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1515 gdouble dist_next = 0.0;
1516 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1518 gdouble dist_between_tps = fabs (dist_next - dist_current);
1519 gdouble ratio = 0.0;
1520 // Prevent division by 0 errors
1521 if ( dist_between_tps > 0.0 )
1522 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1524 if ( tp_current && tp_next ) {
1525 // Construct the name based on the distance value
1528 switch (dist_units) {
1529 case VIK_UNITS_DISTANCE_MILES:
1530 units = g_strdup ( _("miles") );
1532 // VIK_UNITS_DISTANCE_KILOMETRES:
1534 units = g_strdup ( _("km") );
1538 // Convert for display
1539 dist_i = distance_in_preferred_units ( dist_i );
1541 // Make the precision of the output related to the unit size.
1543 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1544 else if ( index == 1 )
1545 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1547 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1550 struct LatLon ll_current, ll_next;
1551 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1552 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1554 // positional interpolation
1555 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1556 // but should be good enough over the small scale that I anticipate usage on
1557 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1558 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1560 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1563 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1564 fgcolour = gdk_color_to_string ( &(trk->color) );
1566 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1568 // if highlight mode on, then colour the background in the highlight colour
1570 if ( drawing_highlight )
1571 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1573 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1575 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1577 g_free ( fgcolour );
1578 g_free ( bgcolour );
1585 * trw_layer_draw_track_name_labels:
1587 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1589 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1592 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1593 fgcolour = gdk_color_to_string ( &(trk->color) );
1595 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1597 // if highlight mode on, then colour the background in the highlight colour
1599 if ( drawing_highlight )
1600 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1602 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1604 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1606 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1607 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1608 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1609 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1610 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1611 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1613 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1615 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1618 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1619 // No other labels to draw
1622 GList *ending = g_list_last ( trk->trackpoints );
1623 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1624 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1626 gboolean done_start_end = FALSE;
1628 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1629 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1631 // This number can be configured via the settings if you really want to change it
1632 gdouble distance_diff;
1633 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1634 distance_diff = 100.0; // Metres
1636 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1637 // Start and end 'close' together so only draw one label at an average location
1638 gint x1, x2, y1, y2;
1639 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1640 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1642 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1644 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1645 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1648 done_start_end = TRUE;
1652 if ( ! done_start_end ) {
1653 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1654 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1655 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1656 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1657 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1658 g_free ( name_start );
1660 // Don't draw end label if this is the one being created
1661 if ( trk != dp->vtl->current_track ) {
1662 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1663 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1664 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1665 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1666 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1667 g_free ( name_end );
1672 g_free ( fgcolour );
1673 g_free ( bgcolour );
1677 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1679 if ( ! track->visible )
1682 /* TODO: this function is a mess, get rid of any redundancy */
1683 GList *list = track->trackpoints;
1685 gboolean useoldvals = TRUE;
1687 gboolean drawpoints;
1689 gboolean drawelevation;
1690 gdouble min_alt, max_alt, alt_diff = 0;
1692 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1693 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1696 if ( dp->vtl->drawelevation )
1698 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1699 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1700 alt_diff = max_alt - min_alt;
1703 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1704 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1705 trw_layer_draw_track ( id, track, dp, TRUE );
1707 if ( draw_track_outline )
1708 drawpoints = drawstops = FALSE;
1710 drawpoints = dp->vtl->drawpoints;
1711 drawstops = dp->vtl->drawstops;
1714 gboolean drawing_highlight = FALSE;
1715 /* Current track - used for creation */
1716 if ( track == dp->vtl->current_track )
1717 main_gc = dp->vtl->current_track_gc;
1719 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1720 /* Draw all tracks of the layer in special colour */
1721 /* if track is member of selected layer or is the current selected track
1722 then draw in the highlight colour.
1723 NB this supercedes the drawmode */
1724 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1725 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1726 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1727 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1728 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1729 drawing_highlight = TRUE;
1732 if ( !drawing_highlight ) {
1733 // Still need to figure out the gc according to the drawing mode:
1734 switch ( dp->vtl->drawmode ) {
1735 case DRAWMODE_BY_TRACK:
1736 if ( dp->vtl->track_1color_gc )
1737 g_object_unref ( dp->vtl->track_1color_gc );
1738 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1739 main_gc = dp->vtl->track_1color_gc;
1742 // Mostly for DRAWMODE_ALL_SAME_COLOR
1743 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1744 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1751 int x, y, oldx, oldy;
1752 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1754 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1756 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1758 // Draw the first point as something a bit different from the normal points
1759 // ATM it's slightly bigger and a triangle
1761 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1762 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1768 gdouble average_speed = 0.0;
1769 gdouble low_speed = 0.0;
1770 gdouble high_speed = 0.0;
1771 // If necessary calculate these values - which is done only once per track redraw
1772 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1773 // the percentage factor away from the average speed determines transistions between the levels
1774 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1775 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1776 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1779 while ((list = g_list_next(list)))
1781 tp = VIK_TRACKPOINT(list->data);
1782 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1784 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1785 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1786 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1787 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1788 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1790 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1793 * If points are the same in display coordinates, don't draw.
1795 if ( useoldvals && x == oldx && y == oldy )
1797 // Still need to process points to ensure 'stops' are drawn if required
1798 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1799 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1800 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 );
1805 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1806 if ( drawpoints || dp->vtl->drawlines ) {
1807 // setup main_gc for both point and line drawing
1808 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1809 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 ) );
1813 if ( drawpoints && ! draw_track_outline )
1818 * The concept of drawing stops is that a trackpoint
1819 * that is if the next trackpoint has a timestamp far into
1820 * the future, we draw a circle of 6x trackpoint size,
1821 * instead of a rectangle of 2x trackpoint size.
1822 * This is drawn first so the trackpoint will be drawn on top
1825 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1826 /* Stop point. Draw 6x circle. Always in redish colour */
1827 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 );
1829 /* Regular point - draw 2x square. */
1830 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1833 /* Final point - draw 4x circle. */
1834 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 );
1837 if ((!tp->newsegment) && (dp->vtl->drawlines))
1840 /* UTM only: zone check */
1841 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1842 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1845 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1847 if ( draw_track_outline ) {
1848 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1852 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1854 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1856 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1861 tmp[1].y = oldy-FIXALTITUDE(list->data);
1863 tmp[2].y = y-FIXALTITUDE(list->next->data);
1868 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1869 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1871 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1872 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1874 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1879 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1880 // Draw an arrow at the mid point to show the direction of the track
1881 // Code is a rework from vikwindow::draw_ruler()
1882 gint midx = (oldx + x) / 2;
1883 gint midy = (oldy + y) / 2;
1885 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1886 // Avoid divide by zero and ensure at least 1 pixel big
1888 gdouble dx = (oldx - midx) / len;
1889 gdouble dy = (oldy - midy) / len;
1890 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1891 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1901 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1903 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1904 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1906 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1908 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1909 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 ));
1913 * If points are the same in display coordinates, don't draw.
1915 if ( x != oldx || y != oldy )
1917 if ( draw_track_outline )
1918 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1920 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1926 * If points are the same in display coordinates, don't draw.
1928 if ( x != oldx && y != oldy )
1930 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1931 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1939 // Labels drawn after the trackpoints, so the labels are on top
1940 if ( dp->vtl->track_draw_labels ) {
1941 if ( track->max_number_dist_labels > 0 ) {
1942 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
1945 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
1946 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
1952 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1954 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1955 trw_layer_draw_track ( id, track, dp, FALSE );
1959 static void cached_pixbuf_free ( CachedPixbuf *cp )
1961 g_object_unref ( G_OBJECT(cp->pixbuf) );
1962 g_free ( cp->image );
1965 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1967 return strcmp ( cp->image, name );
1970 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1973 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1974 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1975 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1978 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1980 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1982 if ( wp->image && dp->vtl->drawimages )
1984 GdkPixbuf *pixbuf = NULL;
1987 if ( dp->vtl->image_alpha == 0)
1990 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1992 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1995 gchar *image = wp->image;
1996 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1997 if ( ! regularthumb )
1999 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2000 image = "\x12\x00"; /* this shouldn't occur naturally. */
2004 CachedPixbuf *cp = NULL;
2005 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2006 if ( dp->vtl->image_size == 128 )
2007 cp->pixbuf = regularthumb;
2010 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2011 g_assert ( cp->pixbuf );
2012 g_object_unref ( G_OBJECT(regularthumb) );
2014 cp->image = g_strdup ( image );
2016 /* needed so 'click picture' tool knows how big the pic is; we don't
2017 * store it in cp because they may have been freed already. */
2018 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2019 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2021 g_queue_push_head ( dp->vtl->image_cache, cp );
2022 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2023 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2025 pixbuf = cp->pixbuf;
2029 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2035 w = gdk_pixbuf_get_width ( pixbuf );
2036 h = gdk_pixbuf_get_height ( pixbuf );
2038 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2040 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2041 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2042 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2043 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2044 // Highlighted - so draw a little border around the chosen one
2045 // single line seems a little weak so draw 2 of them
2046 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2047 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2048 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2049 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2052 if ( dp->vtl->image_alpha == 255 )
2053 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2055 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2057 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2061 // Draw appropriate symbol - either symbol image or simple types
2062 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2063 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 );
2065 else if ( wp == dp->vtl->current_wp ) {
2066 switch ( dp->vtl->wp_symbol ) {
2067 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;
2068 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;
2069 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;
2070 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 );
2071 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 );
2075 switch ( dp->vtl->wp_symbol ) {
2076 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;
2077 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;
2078 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;
2079 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 );
2080 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;
2084 if ( dp->vtl->drawlabels )
2086 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2087 gint label_x, label_y;
2089 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2091 // Could this stored in the waypoint rather than recreating each pass?
2092 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2094 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2095 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2097 // Fallback if parse failure
2098 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2100 g_free ( wp_label_markup );
2102 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2103 label_x = x - width/2;
2104 if ( wp->symbol_pixbuf )
2105 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2107 label_y = y - dp->vtl->wp_size - height - 2;
2109 /* if highlight mode on, then draw background text in highlight colour */
2110 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2111 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2112 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2113 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2114 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2116 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2119 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2121 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2126 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2128 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2129 trw_layer_draw_waypoint ( id, wp, dp );
2133 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2135 static struct DrawingParams dp;
2136 g_assert ( l != NULL );
2138 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2140 if ( l->tracks_visible )
2141 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2143 if ( l->routes_visible )
2144 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2146 if (l->waypoints_visible)
2147 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2150 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2153 if ( vtl->track_bg_gc )
2155 g_object_unref ( vtl->track_bg_gc );
2156 vtl->track_bg_gc = NULL;
2158 if ( vtl->track_1color_gc )
2160 g_object_unref ( vtl->track_1color_gc );
2161 vtl->track_1color_gc = NULL;
2163 if ( vtl->current_track_gc )
2165 g_object_unref ( vtl->current_track_gc );
2166 vtl->current_track_gc = NULL;
2168 if ( vtl->current_track_newpoint_gc )
2170 g_object_unref ( vtl->current_track_newpoint_gc );
2171 vtl->current_track_newpoint_gc = NULL;
2174 if ( ! vtl->track_gc )
2176 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2177 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2178 g_array_free ( vtl->track_gc, TRUE );
2179 vtl->track_gc = NULL;
2182 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2184 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2185 gint width = vtl->line_thickness;
2187 if ( vtl->track_gc )
2188 trw_layer_free_track_gcs ( vtl );
2190 if ( vtl->track_bg_gc )
2191 g_object_unref ( vtl->track_bg_gc );
2192 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2194 // Ensure new track drawing heeds line thickness setting
2195 // however always have a minium of 2, as 1 pixel is really narrow
2196 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2198 if ( vtl->current_track_gc )
2199 g_object_unref ( vtl->current_track_gc );
2200 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2201 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2203 // 'newpoint' gc is exactly the same as the current track gc
2204 if ( vtl->current_track_newpoint_gc )
2205 g_object_unref ( vtl->current_track_newpoint_gc );
2206 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2207 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2209 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2211 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2212 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2214 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2215 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2216 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2218 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2220 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2223 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2225 VikTrwLayer *rv = trw_layer_new1 ( vp );
2226 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2228 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2229 /* early exit, as the rest is GUI related */
2233 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2234 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2236 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2237 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2239 trw_layer_new_track_gcs ( rv, vp );
2241 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2242 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2243 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2244 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2246 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2248 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2253 #define SMALL_ICON_SIZE 18
2255 * Can accept a null symbol, and may return null value
2257 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2259 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2260 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2261 // So needing a small icon for the treeview may need some resizing:
2262 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2263 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2267 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2269 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2271 GdkPixbuf *pixbuf = NULL;
2273 if ( track->has_color ) {
2274 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2275 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2276 // Here is some magic found to do the conversion
2277 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2278 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2279 ((track->color.green & 0xff00) << 8) |
2280 (track->color.blue & 0xff00);
2282 gdk_pixbuf_fill ( pixbuf, pixel );
2285 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 );
2288 g_object_unref (pixbuf);
2290 *new_iter = *((GtkTreeIter *) pass_along[1]);
2291 if ( track->is_route )
2292 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2294 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2296 if ( ! track->visible )
2297 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2300 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2302 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2304 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 );
2306 *new_iter = *((GtkTreeIter *) pass_along[1]);
2307 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2309 if ( ! wp->visible )
2310 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2313 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2315 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2318 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2320 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2323 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2325 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2328 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2331 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2333 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2334 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2336 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2338 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2341 if ( g_hash_table_size (vtl->routes) > 0 ) {
2342 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2344 pass_along[0] = &(vtl->routes_iter);
2345 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2347 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2349 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2352 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2353 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2355 pass_along[0] = &(vtl->waypoints_iter);
2356 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2358 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2360 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2365 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2369 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2370 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2371 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2372 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2374 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2376 return (t->visible ^= 1);
2380 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2382 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2384 return (t->visible ^= 1);
2388 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2390 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2392 return (t->visible ^= 1);
2401 * Return a property about tracks for this layer
2403 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2405 return vtl->line_thickness;
2408 // Structure to hold multiple track information for a layer
2417 * Build up layer multiple track information via updating the tooltip_tracks structure
2419 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2421 tt->length = tt->length + vik_track_get_length (tr);
2423 // Ensure times are available
2424 if ( tr->trackpoints &&
2425 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2426 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2429 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2430 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2432 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2433 // Hence initialize to the first 'proper' value
2434 if ( tt->start_time == 0 )
2435 tt->start_time = t1;
2436 if ( tt->end_time == 0 )
2439 // Update find the earliest / last times
2440 if ( t1 < tt->start_time )
2441 tt->start_time = t1;
2442 if ( t2 > tt->end_time )
2445 // Keep track of total time
2446 // there maybe gaps within a track (eg segments)
2447 // but this should be generally good enough for a simple indicator
2448 tt->duration = tt->duration + (int)(t2-t1);
2453 * Generate tooltip text for the layer.
2454 * This is relatively complicated as it considers information for
2455 * no tracks, a single track or multiple tracks
2456 * (which may or may not have timing information)
2458 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2469 static gchar tmp_buf[128];
2472 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2474 // Safety check - I think these should always be valid
2475 if ( vtl->tracks && vtl->waypoints ) {
2476 tooltip_tracks tt = { 0.0, 0, 0 };
2477 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2479 GDate* gdate_start = g_date_new ();
2480 g_date_set_time_t (gdate_start, tt.start_time);
2482 GDate* gdate_end = g_date_new ();
2483 g_date_set_time_t (gdate_end, tt.end_time);
2485 if ( g_date_compare (gdate_start, gdate_end) ) {
2486 // Dates differ so print range on separate line
2487 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2488 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2489 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2492 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2493 if ( tt.start_time != 0 )
2494 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2498 if ( tt.length > 0.0 ) {
2499 gdouble len_in_units;
2501 // Setup info dependent on distance units
2502 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2503 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2504 len_in_units = VIK_METERS_TO_MILES(tt.length);
2507 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2508 len_in_units = tt.length/1000.0;
2511 // Timing information if available
2513 if ( tt.duration > 0 ) {
2514 g_snprintf (tbuf1, sizeof(tbuf1),
2515 _(" in %d:%02d hrs:mins"),
2516 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2518 g_snprintf (tbuf2, sizeof(tbuf2),
2519 _("\n%sTotal Length %.1f %s%s"),
2520 tbuf3, len_in_units, tbuf4, tbuf1);
2523 // Put together all the elements to form compact tooltip text
2524 g_snprintf (tmp_buf, sizeof(tmp_buf),
2525 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2526 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2528 g_date_free (gdate_start);
2529 g_date_free (gdate_end);
2536 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2540 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2542 // Very simple tooltip - may expand detail in the future...
2543 static gchar tmp_buf[32];
2544 g_snprintf (tmp_buf, sizeof(tmp_buf),
2546 g_hash_table_size (l->tracks));
2550 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2552 // Very simple tooltip - may expand detail in the future...
2553 static gchar tmp_buf[32];
2554 g_snprintf (tmp_buf, sizeof(tmp_buf),
2556 g_hash_table_size (l->routes));
2561 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2562 // Same tooltip for a route
2563 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2566 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2567 tr = g_hash_table_lookup ( l->tracks, sublayer );
2569 tr = g_hash_table_lookup ( l->routes, sublayer );
2572 // Could be a better way of handling strings - but this works...
2573 gchar time_buf1[20];
2574 gchar time_buf2[20];
2575 time_buf1[0] = '\0';
2576 time_buf2[0] = '\0';
2577 static gchar tmp_buf[100];
2578 // Compact info: Short date eg (11/20/99), duration and length
2579 // Hopefully these are the things that are most useful and so promoted into the tooltip
2580 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2581 // %x The preferred date representation for the current locale without the time.
2582 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2583 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2584 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2586 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2589 // Get length and consider the appropriate distance units
2590 gdouble tr_len = vik_track_get_length(tr);
2591 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2592 switch (dist_units) {
2593 case VIK_UNITS_DISTANCE_KILOMETRES:
2594 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2596 case VIK_UNITS_DISTANCE_MILES:
2597 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2606 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2608 // Very simple tooltip - may expand detail in the future...
2609 static gchar tmp_buf[32];
2610 g_snprintf (tmp_buf, sizeof(tmp_buf),
2612 g_hash_table_size (l->waypoints));
2616 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2618 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2619 // NB It's OK to return NULL
2624 return w->description;
2633 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2636 * set_statusbar_msg_info_trkpt:
2638 * Function to show track point information on the statusbar
2639 * Items displayed is controlled by the settings format code
2641 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2643 gchar *statusbar_format_code = NULL;
2644 gboolean need2free = FALSE;
2645 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2646 // Otherwise use default
2647 statusbar_format_code = g_strdup ( "KATDN" );
2651 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2652 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2656 g_free ( statusbar_format_code );
2660 * Function to show basic waypoint information on the statusbar
2662 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2665 switch (a_vik_get_units_height ()) {
2666 case VIK_UNITS_HEIGHT_FEET:
2667 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2670 //VIK_UNITS_HEIGHT_METRES:
2671 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2675 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2676 // one can easily use the current pointer position to see this if needed
2677 gchar *lat = NULL, *lon = NULL;
2678 static struct LatLon ll;
2679 vik_coord_to_latlon (&(wpt->coord), &ll);
2680 a_coords_latlon_to_string ( &ll, &lat, &lon );
2682 // Combine parts to make overall message
2685 // Add comment if available
2686 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2688 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2689 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2696 * General layer selection function, find out which bit is selected and take appropriate action
2698 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2701 l->current_wp = NULL;
2702 l->current_wp_id = NULL;
2703 trw_layer_cancel_current_tp ( l, FALSE );
2706 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2710 case VIK_TREEVIEW_TYPE_LAYER:
2712 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2713 /* Mark for redraw */
2718 case VIK_TREEVIEW_TYPE_SUBLAYER:
2722 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2724 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2725 /* Mark for redraw */
2729 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2731 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2732 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2733 /* Mark for redraw */
2737 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2739 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2740 /* Mark for redraw */
2744 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2746 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2747 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2748 /* Mark for redraw */
2752 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2754 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2755 /* Mark for redraw */
2759 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2761 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2763 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2764 // Show some waypoint info
2765 set_statusbar_msg_info_wpt ( l, wpt );
2766 /* Mark for redraw */
2773 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2782 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2787 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2792 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2797 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2799 return l->waypoints;
2802 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2804 return vtl->tracks_iters;
2807 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2809 return vtl->routes_iters;
2812 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2814 return vtl->waypoints;
2817 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2819 return ! ( g_hash_table_size ( vtl->tracks ) ||
2820 g_hash_table_size ( vtl->routes ) ||
2821 g_hash_table_size ( vtl->waypoints ) );
2824 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2826 return vtl->tracks_visible;
2829 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2831 return vtl->routes_visible;
2834 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2836 return vtl->waypoints_visible;
2840 * ATM use a case sensitive find
2841 * Finds the first one
2843 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2845 if ( wp && wp->name )
2846 if ( ! strcmp ( wp->name, name ) )
2852 * Get waypoint by name - not guaranteed to be unique
2853 * Finds the first one
2855 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2857 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2861 * ATM use a case sensitive find
2862 * Finds the first one
2864 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2866 if ( trk && trk->name )
2867 if ( ! strcmp ( trk->name, name ) )
2873 * Get track by name - not guaranteed to be unique
2874 * Finds the first one
2876 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2878 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2882 * Get route by name - not guaranteed to be unique
2883 * Finds the first one
2885 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2887 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2890 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2892 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2893 maxmin[0].lat = trk->bbox.north;
2894 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2895 maxmin[1].lat = trk->bbox.south;
2896 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2897 maxmin[0].lon = trk->bbox.east;
2898 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2899 maxmin[1].lon = trk->bbox.west;
2902 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2904 // Continually reuse maxmin to find the latest maximum and minimum values
2905 // First set to waypoints bounds
2906 maxmin[0].lat = vtl->waypoints_bbox.north;
2907 maxmin[1].lat = vtl->waypoints_bbox.south;
2908 maxmin[0].lon = vtl->waypoints_bbox.east;
2909 maxmin[1].lon = vtl->waypoints_bbox.west;
2910 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2911 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2914 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2916 /* 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... */
2917 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2918 trw_layer_find_maxmin (vtl, maxmin);
2919 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2923 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2924 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2929 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2932 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2933 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2935 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2938 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2940 /* First set the center [in case previously viewing from elsewhere] */
2941 /* Then loop through zoom levels until provided positions are in view */
2942 /* This method is not particularly fast - but should work well enough */
2943 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2945 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2946 vik_viewport_set_center_coord ( vvp, &coord );
2948 /* Convert into definite 'smallest' and 'largest' positions */
2949 struct LatLon minmin;
2950 if ( maxmin[0].lat < maxmin[1].lat )
2951 minmin.lat = maxmin[0].lat;
2953 minmin.lat = maxmin[1].lat;
2955 struct LatLon maxmax;
2956 if ( maxmin[0].lon > maxmin[1].lon )
2957 maxmax.lon = maxmin[0].lon;
2959 maxmax.lon = maxmin[1].lon;
2961 /* Never zoom in too far - generally not that useful, as too close ! */
2962 /* Always recalculate the 'best' zoom level */
2964 vik_viewport_set_zoom ( vvp, zoom );
2966 gdouble min_lat, max_lat, min_lon, max_lon;
2967 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2968 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2969 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2970 /* NB I think the logic used in this test to determine if the bounds is within view
2971 fails if track goes across 180 degrees longitude.
2972 Hopefully that situation is not too common...
2973 Mind you viking doesn't really do edge locations to well anyway */
2974 if ( min_lat < minmin.lat &&
2975 max_lat > minmin.lat &&
2976 min_lon < maxmax.lon &&
2977 max_lon > maxmax.lon )
2978 /* Found within zoom level */
2983 vik_viewport_set_zoom ( vvp, zoom );
2987 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2989 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2990 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2991 trw_layer_find_maxmin (vtl, maxmin);
2992 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2995 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3000 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
3002 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])) ) ) {
3003 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3006 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3009 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3011 GtkWidget *file_selector;
3013 gboolean failed = FALSE;
3014 file_selector = gtk_file_chooser_dialog_new (title,
3016 GTK_FILE_CHOOSER_ACTION_SAVE,
3017 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3018 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3020 gchar *cwd = g_get_current_dir();
3022 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3026 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3028 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3030 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3031 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
3033 gtk_widget_hide ( file_selector );
3034 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3035 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3036 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3041 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3043 gtk_widget_hide ( file_selector );
3044 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3045 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3046 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3051 gtk_widget_destroy ( file_selector );
3053 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3056 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3058 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3061 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3063 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3066 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3068 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3069 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3070 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3071 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3073 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3075 g_free ( auto_save_name );
3078 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3080 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3081 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3082 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3083 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3085 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3087 g_free ( auto_save_name );
3091 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3094 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3096 gchar *name_used = NULL;
3099 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3100 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3101 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3102 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3104 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3108 gchar *quoted_file = g_shell_quote ( name_used );
3109 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3110 g_free ( quoted_file );
3111 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3113 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3114 g_error_free ( err );
3118 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3119 //g_remove ( name_used );
3120 // Perhaps should be deleted when the program ends?
3121 // For now leave it to the user to delete it / use system temp cleanup methods.
3122 g_free ( name_used );
3126 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3128 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3131 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3133 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3136 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3138 gpointer layer_and_vlp[2];
3139 layer_and_vlp[0] = pass_along[0];
3140 layer_and_vlp[1] = pass_along[1];
3142 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3144 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3145 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3147 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3149 if ( !trk || !trk->name )
3152 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3153 gchar *auto_save_name = g_strdup ( trk->name );
3154 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3155 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3157 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
3159 g_free ( auto_save_name );
3163 VikWaypoint *wp; // input
3164 gpointer uuid; // output
3167 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3169 wpu_udata *user_data = udata;
3170 if ( wp == user_data->wp ) {
3171 user_data->uuid = id;
3177 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3179 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3180 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3181 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3183 GTK_RESPONSE_REJECT,
3185 GTK_RESPONSE_ACCEPT,
3188 GtkWidget *label, *entry;
3189 label = gtk_label_new(_("Waypoint Name:"));
3190 entry = gtk_entry_new();
3192 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3193 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3194 gtk_widget_show_all ( label );
3195 gtk_widget_show_all ( entry );
3197 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3199 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3201 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3202 // Find *first* wp with the given name
3203 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3206 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3209 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3210 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3212 // Find and select on the side panel
3217 // Hmmm, want key of it
3218 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3220 if ( wpf && udata.uuid ) {
3221 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3222 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3231 gtk_widget_destroy ( dia );
3234 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3236 gchar *default_name = highest_wp_number_get(vtl);
3237 VikWaypoint *wp = vik_waypoint_new();
3238 gchar *returned_name;
3240 wp->coord = *def_coord;
3242 // Attempt to auto set height if DEM data is available
3243 vik_waypoint_apply_dem_data ( wp, TRUE );
3245 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3247 if ( returned_name )
3250 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3251 g_free (default_name);
3252 g_free (returned_name);
3255 g_free (default_name);
3256 vik_waypoint_free(wp);
3260 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3262 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3263 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3264 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3265 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3266 VikViewport *vvp = vik_window_viewport(vw);
3268 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3269 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3270 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3271 trw_layer_calculate_bounds_waypoints ( vtl );
3272 vik_layers_panel_emit_update ( vlp );
3275 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3277 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3278 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3279 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3281 trw_layer_find_maxmin (vtl, maxmin);
3282 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3283 trw_layer_calculate_bounds_waypoints ( vtl );
3284 vik_layers_panel_emit_update ( vlp );
3287 #ifdef VIK_CONFIG_GEOTAG
3288 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3290 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3292 // Update directly - not changing the mtime
3293 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3296 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3298 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3301 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3305 * Use code in separate file for this feature as reasonably complex
3307 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3309 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3310 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3311 // Unset so can be reverified later if necessary
3312 vtl->has_verified_thumbnails = FALSE;
3314 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3320 static void trw_layer_geotagging ( gpointer lav[2] )
3322 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3323 // Unset so can be reverified later if necessary
3324 vtl->has_verified_thumbnails = FALSE;
3326 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3333 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3336 * Acquire into this TRW Layer straight from GPS Device
3338 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3340 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3341 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3342 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3343 VikViewport *vvp = vik_window_viewport(vw);
3345 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3346 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3350 * Acquire into this TRW Layer from Directions
3352 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3354 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3355 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3356 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3357 VikViewport *vvp = vik_window_viewport(vw);
3359 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3362 #ifdef VIK_CONFIG_OPENSTREETMAP
3364 * Acquire into this TRW Layer from OSM
3366 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3368 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3369 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3370 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3371 VikViewport *vvp = vik_window_viewport(vw);
3373 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3377 * Acquire into this TRW Layer from OSM for 'My' Traces
3379 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3381 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3382 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3383 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3384 VikViewport *vvp = vik_window_viewport(vw);
3386 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3390 #ifdef VIK_CONFIG_GEOCACHES
3392 * Acquire into this TRW Layer from Geocaching.com
3394 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3396 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3397 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3398 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3399 VikViewport *vvp = vik_window_viewport(vw);
3401 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3405 #ifdef VIK_CONFIG_GEOTAG
3407 * Acquire into this TRW Layer from images
3409 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3411 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3412 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3413 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3414 VikViewport *vvp = vik_window_viewport(vw);
3416 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3417 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3419 // Reverify thumbnails as they may have changed
3420 vtl->has_verified_thumbnails = FALSE;
3421 trw_layer_verify_thumbnails ( vtl, NULL );
3425 static void trw_layer_gps_upload ( gpointer lav[2] )
3427 gpointer pass_along[6];
3428 pass_along[0] = lav[0];
3429 pass_along[1] = lav[1];
3430 pass_along[2] = NULL; // No track - operate on the layer
3431 pass_along[3] = NULL;
3432 pass_along[4] = NULL;
3433 pass_along[5] = NULL;
3435 trw_layer_gps_upload_any ( pass_along );
3439 * If pass_along[3] is defined that this will upload just that track
3441 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3443 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3444 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3446 // May not actually get a track here as pass_along[2&3] can be null
3447 VikTrack *track = NULL;
3448 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3449 gboolean xfer_all = FALSE;
3451 if ( pass_along[2] ) {
3453 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3454 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3457 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3458 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3461 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3464 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3468 else if ( !pass_along[4] )
3469 xfer_all = TRUE; // i.e. whole layer
3471 if (track && !track->visible) {
3472 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3476 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3477 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3478 GTK_DIALOG_DESTROY_WITH_PARENT,
3480 GTK_RESPONSE_ACCEPT,
3482 GTK_RESPONSE_REJECT,
3485 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3486 GtkWidget *response_w = NULL;
3487 #if GTK_CHECK_VERSION (2, 20, 0)
3488 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3492 gtk_widget_grab_focus ( response_w );
3494 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3496 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3497 datasource_gps_clean_up ( dgs );
3498 gtk_widget_destroy ( dialog );
3502 // Get info from reused datasource dialog widgets
3503 gchar* protocol = datasource_gps_get_protocol ( dgs );
3504 gchar* port = datasource_gps_get_descriptor ( dgs );
3505 // NB don't free the above strings as they're references to values held elsewhere
3506 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3507 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3508 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3509 gboolean turn_off = datasource_gps_get_off ( dgs );
3511 gtk_widget_destroy ( dialog );
3513 // When called from the viewport - work the corresponding layerspanel:
3515 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3518 // Apply settings to transfer to the GPS device
3525 vik_layers_panel_get_viewport (vlp),
3534 * Acquire into this TRW Layer from any GPS Babel supported file
3536 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3538 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3539 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3540 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3541 VikViewport *vvp = vik_window_viewport(vw);
3543 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3546 static void trw_layer_new_wp ( gpointer lav[2] )
3548 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3549 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3550 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3551 instead return true if you want to update. */
3552 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 ) {
3553 trw_layer_calculate_bounds_waypoints ( vtl );
3554 vik_layers_panel_emit_update ( vlp );
3558 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3560 vtl->current_track = vik_track_new();
3561 vik_track_set_defaults ( vtl->current_track );
3562 vtl->current_track->visible = TRUE;
3563 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3564 // Create track with the preferred colour from the layer properties
3565 vtl->current_track->color = vtl->track_color;
3567 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3568 vtl->current_track->has_color = TRUE;
3569 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3572 static void trw_layer_new_track ( gpointer lav[2] )
3574 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3576 if ( ! vtl->current_track ) {
3577 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3578 new_track_create_common ( vtl, name );
3581 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3585 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3587 vtl->current_track = vik_track_new();
3588 vik_track_set_defaults ( vtl->current_track );
3589 vtl->current_track->visible = TRUE;
3590 vtl->current_track->is_route = TRUE;
3591 // By default make all routes red
3592 vtl->current_track->has_color = TRUE;
3593 gdk_color_parse ( "red", &vtl->current_track->color );
3594 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3597 static void trw_layer_new_route ( gpointer lav[2] )
3599 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3601 if ( ! vtl->current_track ) {
3602 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3603 new_route_create_common ( vtl, name );
3605 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3609 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3611 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3612 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3614 if ( g_hash_table_size (vtl->routes) > 0 ) {
3615 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3616 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3617 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3618 vik_layers_panel_emit_update ( vlp );
3623 static void trw_layer_finish_track ( gpointer lav[2] )
3625 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3626 vtl->current_track = NULL;
3627 vik_layer_emit_update ( VIK_LAYER(vtl) );
3630 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3632 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3633 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3635 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3636 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3637 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3638 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3639 vik_layers_panel_emit_update ( vlp );
3643 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3645 /* NB do not care if wp is visible or not */
3646 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3649 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3651 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3652 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3654 /* Only 1 waypoint - jump straight to it */
3655 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3656 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3657 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3659 /* If at least 2 waypoints - find center and then zoom to fit */
3660 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3662 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3663 maxmin[0].lat = vtl->waypoints_bbox.north;
3664 maxmin[1].lat = vtl->waypoints_bbox.south;
3665 maxmin[0].lon = vtl->waypoints_bbox.east;
3666 maxmin[1].lon = vtl->waypoints_bbox.west;
3667 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3670 vik_layers_panel_emit_update ( vlp );
3673 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3675 static gpointer pass_along[2];
3677 GtkWidget *export_submenu;
3678 pass_along[0] = vtl;
3679 pass_along[1] = vlp;
3681 item = gtk_menu_item_new();
3682 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3683 gtk_widget_show ( item );
3685 if ( vtl->current_track ) {
3686 if ( vtl->current_track->is_route )
3687 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3689 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3691 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3692 gtk_widget_show ( item );
3695 item = gtk_menu_item_new ();
3696 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3697 gtk_widget_show ( item );
3700 /* Now with icons */
3701 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3702 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3703 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3704 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3705 gtk_widget_show ( item );
3707 GtkWidget *view_submenu = gtk_menu_new();
3708 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3709 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3710 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3711 gtk_widget_show ( item );
3712 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3714 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3716 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3717 gtk_widget_show ( item );
3719 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3720 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3721 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3722 gtk_widget_show ( item );
3724 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3726 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3727 gtk_widget_show ( item );
3729 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3730 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3731 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3732 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3733 gtk_widget_show ( item );
3735 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3736 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3737 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3738 gtk_widget_show ( item );
3740 export_submenu = gtk_menu_new ();
3741 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3742 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3743 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3744 gtk_widget_show ( item );
3745 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3747 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3748 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3749 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3750 gtk_widget_show ( item );
3752 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3754 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3755 gtk_widget_show ( item );
3757 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3759 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3760 gtk_widget_show ( item );
3762 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3764 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3765 gtk_widget_show ( item );
3767 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3768 item = gtk_menu_item_new_with_mnemonic ( external1 );
3769 g_free ( external1 );
3770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3771 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3772 gtk_widget_show ( item );
3774 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3775 item = gtk_menu_item_new_with_mnemonic ( external2 );
3776 g_free ( external2 );
3777 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3778 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3779 gtk_widget_show ( item );
3781 GtkWidget *new_submenu = gtk_menu_new();
3782 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3783 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3784 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3785 gtk_widget_show(item);
3786 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3788 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3789 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3790 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3791 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3792 gtk_widget_show ( item );
3794 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3797 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3798 gtk_widget_show ( item );
3799 // Make it available only when a new track *not* already in progress
3800 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3802 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3803 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3805 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3806 gtk_widget_show ( item );
3807 // Make it available only when a new track *not* already in progress
3808 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3810 #ifdef VIK_CONFIG_GEOTAG
3811 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3812 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3813 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3814 gtk_widget_show ( item );
3817 GtkWidget *acquire_submenu = gtk_menu_new ();
3818 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3819 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3820 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3821 gtk_widget_show ( item );
3822 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3824 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3825 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3826 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3827 gtk_widget_show ( item );
3829 /* FIXME: only add menu when at least a routing engine has support for Directions */
3830 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3832 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3833 gtk_widget_show ( item );
3835 #ifdef VIK_CONFIG_OPENSTREETMAP
3836 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3837 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3838 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3839 gtk_widget_show ( item );
3841 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3843 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3844 gtk_widget_show ( item );
3847 #ifdef VIK_CONFIG_GEONAMES
3848 GtkWidget *wikipedia_submenu = gtk_menu_new();
3849 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3850 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3851 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3852 gtk_widget_show(item);
3853 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3855 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3856 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3857 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3858 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3859 gtk_widget_show ( item );
3861 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3862 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3863 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3864 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3865 gtk_widget_show ( item );
3868 #ifdef VIK_CONFIG_GEOCACHES
3869 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3870 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3871 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3872 gtk_widget_show ( item );
3875 #ifdef VIK_CONFIG_GEOTAG
3876 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3877 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3878 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3879 gtk_widget_show ( item );
3882 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3883 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3884 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3885 gtk_widget_show ( item );
3887 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3889 GtkWidget *upload_submenu = gtk_menu_new ();
3890 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3891 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3892 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3893 gtk_widget_show ( item );
3894 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3896 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3897 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3899 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3900 gtk_widget_show ( item );
3902 #ifdef VIK_CONFIG_OPENSTREETMAP
3903 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3904 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3905 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3906 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3907 gtk_widget_show ( item );
3910 GtkWidget *delete_submenu = gtk_menu_new ();
3911 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3912 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3913 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3914 gtk_widget_show ( item );
3915 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3917 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3918 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3919 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3920 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3921 gtk_widget_show ( item );
3923 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3924 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3925 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3926 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3927 gtk_widget_show ( item );
3929 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3930 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3931 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3932 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3933 gtk_widget_show ( item );
3935 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3936 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3937 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3938 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3939 gtk_widget_show ( item );
3941 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3942 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3944 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3945 gtk_widget_show ( item );
3947 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3948 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3949 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3950 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3951 gtk_widget_show ( item );
3953 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3954 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3956 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3957 gtk_widget_show ( item );
3960 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3961 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3963 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3964 gtk_widget_show ( item );
3968 // Fake Waypoint UUIDs vi simple increasing integer
3969 static guint wp_uuid = 0;
3971 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3975 vik_waypoint_set_name (wp, name);
3977 if ( VIK_LAYER(vtl)->realized )
3979 // Do we need to create the sublayer:
3980 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3981 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3984 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3986 // Visibility column always needed for waypoints
3987 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 );
3989 // Actual setting of visibility dependent on the waypoint
3990 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3992 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3994 // Sort now as post_read is not called on a realized waypoint
3995 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
3998 highest_wp_number_add_wp(vtl, name);
3999 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4003 // Fake Track UUIDs vi simple increasing integer
4004 static guint tr_uuid = 0;
4006 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4010 vik_track_set_name (t, name);
4012 if ( VIK_LAYER(vtl)->realized )
4014 // Do we need to create the sublayer:
4015 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4016 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4019 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4020 // Visibility column always needed for tracks
4021 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 );
4023 // Actual setting of visibility dependent on the track
4024 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4026 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4028 // Sort now as post_read is not called on a realized track
4029 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4032 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4034 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
4037 // Fake Route UUIDs vi simple increasing integer
4038 static guint rt_uuid = 0;
4040 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4044 vik_track_set_name (t, name);
4046 if ( VIK_LAYER(vtl)->realized )
4048 // Do we need to create the sublayer:
4049 if ( g_hash_table_size (vtl->routes) == 0 ) {
4050 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4053 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4054 // Visibility column always needed for routes
4055 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 );
4056 // Actual setting of visibility dependent on the route
4057 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4059 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4061 // Sort now as post_read is not called on a realized route
4062 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4065 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4067 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
4070 /* to be called whenever a track has been deleted or may have been changed. */
4071 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4073 if (vtl->current_tp_track == trk )
4074 trw_layer_cancel_current_tp ( vtl, FALSE );
4078 * Normally this is done to due the waypoint size preference having changed
4080 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4082 GHashTableIter iter;
4083 gpointer key, value;
4086 g_hash_table_iter_init ( &iter, vtl->waypoints );
4087 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4088 VikWaypoint *wp = VIK_WAYPOINT(value);
4090 // Reapply symbol setting to update the pixbuf
4091 gchar *tmp_symbol = g_strdup ( wp->symbol );
4092 vik_waypoint_set_symbol ( wp, tmp_symbol );
4093 g_free ( tmp_symbol );
4099 * trw_layer_new_unique_sublayer_name:
4101 * Allocates a unique new name
4103 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4106 gchar *newname = g_strdup(name);
4111 switch ( sublayer_type ) {
4112 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4113 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4115 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4116 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4119 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4122 // If found a name already in use try adding 1 to it and we try again
4124 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4126 newname = new_newname;
4129 } while ( id != NULL);
4134 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4136 // No more uniqueness of name forced when loading from a file
4137 // This now makes this function a little redunant as we just flow the parameters through
4138 vik_trw_layer_add_waypoint ( vtl, name, wp );
4141 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4143 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4144 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4145 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4146 vik_track_free ( tr );
4147 vtl->route_finder_append = FALSE; /* this means we have added it */
4150 // No more uniqueness of name forced when loading from a file
4152 vik_trw_layer_add_route ( vtl, name, tr );
4154 vik_trw_layer_add_track ( vtl, name, tr );
4156 if ( vtl->route_finder_check_added_track ) {
4157 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4158 vtl->route_finder_added_track = tr;
4163 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4165 *l = g_list_append(*l, id);
4169 * Move an item from one TRW layer to another TRW layer
4171 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4173 // TODO reconsider strategy when moving within layer (if anything...)
4174 gboolean rename = ( vtl_src != vtl_dest );
4178 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4179 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4183 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4185 newname = g_strdup ( trk->name );
4187 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4188 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4190 vik_trw_layer_delete_track ( vtl_src, trk );
4193 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4194 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4198 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4200 newname = g_strdup ( trk->name );
4202 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4203 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4205 vik_trw_layer_delete_route ( vtl_src, trk );
4208 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4209 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4213 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4215 newname = g_strdup ( wp->name );
4217 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4218 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4220 trw_layer_delete_waypoint ( vtl_src, wp );
4222 // Recalculate bounds even if not renamed as maybe dragged between layers
4223 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4224 trw_layer_calculate_bounds_waypoints ( vtl_src );
4228 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4230 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4231 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4233 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4234 GList *items = NULL;
4237 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4238 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4240 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4241 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4243 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4244 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4249 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4250 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4251 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4252 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4254 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4261 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4262 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4266 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4268 trku_udata *user_data = udata;
4269 if ( trk == user_data->trk ) {
4270 user_data->uuid = id;
4276 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4278 gboolean was_visible = FALSE;
4280 if ( trk && trk->name ) {
4282 if ( trk == vtl->current_track ) {
4283 vtl->current_track = NULL;
4284 vtl->current_tp_track = NULL;
4285 vtl->current_tp_id = NULL;
4286 vtl->moving_tp = FALSE;
4289 was_visible = trk->visible;
4291 if ( trk == vtl->route_finder_current_track )
4292 vtl->route_finder_current_track = NULL;
4294 if ( trk == vtl->route_finder_added_track )
4295 vtl->route_finder_added_track = NULL;
4301 // Hmmm, want key of it
4302 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4304 if ( trkf && udata.uuid ) {
4305 /* could be current_tp, so we have to check */
4306 trw_layer_cancel_tps_of_track ( vtl, trk );
4308 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4311 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4312 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4313 g_hash_table_remove ( vtl->tracks, udata.uuid );
4315 // If last sublayer, then remove sublayer container
4316 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4317 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4325 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4327 gboolean was_visible = FALSE;
4329 if ( trk && trk->name ) {
4331 if ( trk == vtl->current_track ) {
4332 vtl->current_track = NULL;
4333 vtl->current_tp_track = NULL;
4334 vtl->current_tp_id = NULL;
4335 vtl->moving_tp = FALSE;
4338 was_visible = trk->visible;
4340 if ( trk == vtl->route_finder_current_track )
4341 vtl->route_finder_current_track = NULL;
4343 if ( trk == vtl->route_finder_added_track )
4344 vtl->route_finder_added_track = NULL;
4350 // Hmmm, want key of it
4351 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4353 if ( trkf && udata.uuid ) {
4354 /* could be current_tp, so we have to check */
4355 trw_layer_cancel_tps_of_track ( vtl, trk );
4357 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4360 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4361 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4362 g_hash_table_remove ( vtl->routes, udata.uuid );
4364 // If last sublayer, then remove sublayer container
4365 if ( g_hash_table_size (vtl->routes) == 0 ) {
4366 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4374 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4376 gboolean was_visible = FALSE;
4378 if ( wp && wp->name ) {
4380 if ( wp == vtl->current_wp ) {
4381 vtl->current_wp = NULL;
4382 vtl->current_wp_id = NULL;
4383 vtl->moving_wp = FALSE;
4386 was_visible = wp->visible;
4392 // Hmmm, want key of it
4393 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4395 if ( wpf && udata.uuid ) {
4396 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4399 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4400 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4402 highest_wp_number_remove_wp(vtl, wp->name);
4403 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4405 // If last sublayer, then remove sublayer container
4406 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4407 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4417 // Only for temporary use by trw_layer_delete_waypoint_by_name
4418 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4420 wpu_udata *user_data = udata;
4421 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4422 user_data->uuid = id;
4429 * Delete a waypoint by the given name
4430 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4431 * as there be multiple waypoints with the same name
4433 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4436 // Fake a waypoint with the given name
4437 udata.wp = vik_waypoint_new ();
4438 vik_waypoint_set_name (udata.wp, name);
4439 // Currently only the name is used in this waypoint find function
4442 // Hmmm, want key of it
4443 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4445 vik_waypoint_free (udata.wp);
4447 if ( wpf && udata.uuid )
4448 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4454 VikTrack *trk; // input
4455 gpointer uuid; // output
4458 // Only for temporary use by trw_layer_delete_track_by_name
4459 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4461 tpu_udata *user_data = udata;
4462 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4463 user_data->uuid = id;
4470 * Delete a track by the given name
4471 * NOTE: ATM this will delete the first encountered Track with the specified name
4472 * as there may be multiple tracks with the same name within the specified hash table
4474 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4477 // Fake a track with the given name
4478 udata.trk = vik_track_new ();
4479 vik_track_set_name (udata.trk, name);
4480 // Currently only the name is used in this waypoint find function
4483 // Hmmm, want key of it
4484 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4486 vik_track_free (udata.trk);
4488 if ( trkf && udata.uuid ) {
4489 // This could be a little better written...
4490 if ( vtl->tracks == ht_tracks )
4491 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4492 if ( vtl->routes == ht_tracks )
4493 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4500 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4502 vik_treeview_item_delete (vt, it );
4505 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4508 vtl->current_track = NULL;
4509 vtl->route_finder_current_track = NULL;
4510 vtl->route_finder_added_track = NULL;
4511 if (vtl->current_tp_track)
4512 trw_layer_cancel_current_tp(vtl, FALSE);
4514 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4515 g_hash_table_remove_all(vtl->routes_iters);
4516 g_hash_table_remove_all(vtl->routes);
4518 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4520 vik_layer_emit_update ( VIK_LAYER(vtl) );
4523 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4526 vtl->current_track = NULL;
4527 vtl->route_finder_current_track = NULL;
4528 vtl->route_finder_added_track = NULL;
4529 if (vtl->current_tp_track)
4530 trw_layer_cancel_current_tp(vtl, FALSE);
4532 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4533 g_hash_table_remove_all(vtl->tracks_iters);
4534 g_hash_table_remove_all(vtl->tracks);
4536 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4538 vik_layer_emit_update ( VIK_LAYER(vtl) );
4541 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4543 vtl->current_wp = NULL;
4544 vtl->current_wp_id = NULL;
4545 vtl->moving_wp = FALSE;
4547 highest_wp_number_reset(vtl);
4549 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4550 g_hash_table_remove_all(vtl->waypoints_iters);
4551 g_hash_table_remove_all(vtl->waypoints);
4553 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4555 vik_layer_emit_update ( VIK_LAYER(vtl) );
4558 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4560 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4561 // Get confirmation from the user
4562 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4563 _("Are you sure you want to delete all tracks in %s?"),
4564 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4565 vik_trw_layer_delete_all_tracks (vtl);
4568 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4570 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4571 // Get confirmation from the user
4572 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4573 _("Are you sure you want to delete all routes in %s?"),
4574 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4575 vik_trw_layer_delete_all_routes (vtl);
4578 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4580 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4581 // Get confirmation from the user
4582 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4583 _("Are you sure you want to delete all waypoints in %s?"),
4584 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4585 vik_trw_layer_delete_all_waypoints (vtl);
4588 static void trw_layer_delete_item ( gpointer pass_along[6] )
4590 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4591 gboolean was_visible = FALSE;
4592 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4594 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4595 if ( wp && wp->name ) {
4596 if ( GPOINTER_TO_INT ( pass_along[4]) )
4597 // Get confirmation from the user
4598 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4599 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4600 _("Are you sure you want to delete the waypoint \"%s\"?"),
4603 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4606 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4608 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4609 if ( trk && trk->name ) {
4610 if ( GPOINTER_TO_INT ( pass_along[4]) )
4611 // Get confirmation from the user
4612 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4613 _("Are you sure you want to delete the track \"%s\"?"),
4616 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4621 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4622 if ( trk && trk->name ) {
4623 if ( GPOINTER_TO_INT ( pass_along[4]) )
4624 // Get confirmation from the user
4625 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4626 _("Are you sure you want to delete the route \"%s\"?"),
4629 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4633 vik_layer_emit_update ( VIK_LAYER(vtl) );
4637 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4639 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4641 vik_waypoint_set_name ( wp, new_name );
4643 // Now update the treeview as well
4648 // Need key of it for treeview update
4649 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4651 if ( wpf && udataU.uuid ) {
4652 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4655 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4656 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4661 static void trw_layer_properties_item ( gpointer pass_along[7] )
4663 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4664 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4666 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4668 if ( wp && wp->name )
4670 gboolean updated = FALSE;
4671 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4673 trw_layer_waypoint_rename ( vtl, wp, new_name );
4675 if ( updated && pass_along[6] )
4676 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4678 if ( updated && VIK_LAYER(vtl)->visible )
4679 vik_layer_emit_update ( VIK_LAYER(vtl) );
4685 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4686 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4688 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4690 if ( tr && tr->name )
4692 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4695 pass_along[1], /* vlp */
4696 pass_along[5], /* vvp */
4697 pass_along[6], /* iter */
4704 * trw_layer_track_statistics:
4706 * Show track statistics.
4707 * ATM jump to the stats page in the properties
4708 * TODO: consider separating the stats into an individual dialog?
4710 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4712 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4714 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4715 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4717 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4719 if ( trk && trk->name ) {
4720 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4723 pass_along[1], // vlp
4724 pass_along[5], // vvp
4725 pass_along[6], // iter
4731 * Update the treeview of the track id - primarily to update the icon
4733 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4739 gpointer *trkf = NULL;
4740 if ( trk->is_route )
4741 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4743 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4745 if ( trkf && udata.uuid ) {
4747 GtkTreeIter *iter = NULL;
4748 if ( trk->is_route )
4749 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4751 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4754 // TODO: Make this a function
4755 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4756 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4757 ((trk->color.green & 0xff00) << 8) |
4758 (trk->color.blue & 0xff00);
4759 gdk_pixbuf_fill ( pixbuf, pixel );
4760 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4761 g_object_unref (pixbuf);
4768 Parameter 1 -> VikLayersPanel
4769 Parameter 2 -> VikLayer
4770 Parameter 3 -> VikViewport
4772 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4775 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4776 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4779 /* since vlp not set, vl & vvp should be valid instead! */
4781 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4782 vik_layer_emit_update ( VIK_LAYER(vl) );
4787 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4789 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4791 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4792 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4794 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4796 if ( track && track->trackpoints )
4797 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4800 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4802 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4804 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4805 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4807 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4809 if ( track && track->trackpoints )
4811 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4813 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4814 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4815 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4816 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4817 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4821 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4823 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4825 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4826 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4828 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4833 // Converting a track to a route can be a bit more complicated,
4834 // so give a chance to change our minds:
4835 if ( !trk->is_route &&
4836 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4837 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4839 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4840 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4845 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4848 trk_copy->is_route = !trk_copy->is_route;
4850 // ATM can't set name to self - so must create temporary copy
4851 gchar *name = g_strdup ( trk_copy->name );
4853 // Delete old one and then add new one
4854 if ( trk->is_route ) {
4855 vik_trw_layer_delete_route ( vtl, trk );
4856 vik_trw_layer_add_track ( vtl, name, trk_copy );
4859 // Extra route conversion bits...
4860 vik_track_merge_segments ( trk_copy );
4861 vik_track_to_routepoints ( trk_copy );
4863 vik_trw_layer_delete_track ( vtl, trk );
4864 vik_trw_layer_add_route ( vtl, name, trk_copy );
4868 // Update in case color of track / route changes when moving between sublayers
4869 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4872 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4874 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4876 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4877 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4879 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4882 vik_track_anonymize_times ( track );
4885 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4887 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4889 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4890 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4892 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4897 vtl->current_track = track;
4898 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);
4900 if ( track->trackpoints )
4901 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4905 * extend a track using route finder
4907 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4909 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4910 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4913 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4915 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4916 vtl->route_finder_coord = last_coord;
4917 vtl->route_finder_current_track = track;
4918 vtl->route_finder_started = TRUE;
4920 if ( track->trackpoints )
4921 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4928 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4930 // If have a vlp then perform a basic test to see if any DEM info available...
4932 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4934 if ( !g_list_length(dems) ) {
4935 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
4943 * apply_dem_data_common:
4945 * A common function for applying the DEM values and reporting the results.
4947 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
4949 if ( !trw_layer_dem_test ( vtl, vlp ) )
4952 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
4953 // Inform user how much was changed
4955 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4956 g_snprintf(str, 64, tmp_str, changed);
4957 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4960 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
4962 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4964 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4965 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4967 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4970 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
4973 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
4975 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4977 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4978 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4980 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4983 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
4989 * A common function for applying the elevation smoothing and reporting the results.
4991 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
4993 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
4994 // Inform user how much was changed
4996 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4997 g_snprintf(str, 64, tmp_str, changed);
4998 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5004 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5006 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5008 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5009 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5011 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5016 smooth_it ( vtl, track, FALSE );
5019 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5021 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5023 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5024 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5026 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5031 smooth_it ( vtl, track, TRUE );
5035 * Commonal helper function
5037 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5040 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5041 g_snprintf(str, 64, tmp_str, changed);
5042 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5045 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5047 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5048 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5050 if ( !trw_layer_dem_test ( vtl, vlp ) )
5054 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5056 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5058 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5062 GHashTableIter iter;
5063 gpointer key, value;
5065 g_hash_table_iter_init ( &iter, vtl->waypoints );
5066 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5067 VikWaypoint *wp = VIK_WAYPOINT(value);
5068 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5071 wp_changed_message ( vtl, changed );
5074 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5076 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5077 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5079 if ( !trw_layer_dem_test ( vtl, vlp ) )
5083 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5085 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5087 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5091 GHashTableIter iter;
5092 gpointer key, value;
5094 g_hash_table_iter_init ( &iter, vtl->waypoints );
5095 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5096 VikWaypoint *wp = VIK_WAYPOINT(value);
5097 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5100 wp_changed_message ( vtl, changed );
5103 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5105 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5107 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5108 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5110 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5115 GList *trps = track->trackpoints;
5118 trps = g_list_last(trps);
5119 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5122 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5124 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5126 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5127 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5129 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5134 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5137 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5140 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5142 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5144 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5145 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5147 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5152 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5155 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5158 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5160 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5162 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5163 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5165 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5170 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5173 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5177 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5179 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5181 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5183 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5184 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5186 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5188 if ( trk && trk->trackpoints )
5190 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5191 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5192 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5193 if ( pass_along[1] )
5194 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5196 vik_layer_emit_update ( VIK_LAYER(vtl) );
5201 * Refine the selected track/route with a routing engine.
5202 * The routing engine is selected by the user, when requestiong the job.
5204 static void trw_layer_route_refine ( gpointer pass_along[6] )
5206 static gint last_engine = 0;
5207 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5210 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5211 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5213 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5215 if ( trk && trk->trackpoints )
5217 /* Check size of the route */
5218 int nb = vik_track_get_tp_count(trk);
5220 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5221 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5222 GTK_MESSAGE_WARNING,
5223 GTK_BUTTONS_OK_CANCEL,
5224 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5226 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5227 gtk_widget_destroy ( dialog );
5228 if (response != GTK_RESPONSE_OK )
5231 /* Select engine from dialog */
5232 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5233 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5234 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5236 GTK_RESPONSE_REJECT,
5238 GTK_RESPONSE_ACCEPT,
5240 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5241 gtk_widget_show_all(label);
5243 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5245 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5246 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5247 gtk_widget_show_all(combo);
5249 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5251 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5253 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5255 /* Dialog validated: retrieve selected engine and do the job */
5256 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5257 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5260 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5262 /* Force saving track */
5263 /* FIXME: remove or rename this hack */
5264 vtl->route_finder_check_added_track = TRUE;
5267 vik_routing_engine_refine (routing, vtl, trk);
5269 /* FIXME: remove or rename this hack */
5270 if ( vtl->route_finder_added_track )
5271 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5273 vtl->route_finder_added_track = NULL;
5274 vtl->route_finder_check_added_track = FALSE;
5276 vik_layer_emit_update ( VIK_LAYER(vtl) );
5278 /* Restore cursor */
5279 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5281 gtk_widget_destroy ( dialog );
5285 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5287 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5288 trw_layer_tpwin_init ( vtl );
5291 /*************************************
5292 * merge/split by time routines
5293 *************************************/
5295 /* called for each key in track hash table.
5296 * If the current track has the same time stamp type, add it to the result,
5297 * except the one pointed by "exclude".
5298 * set exclude to NULL if there is no exclude to check.
5299 * Note that the result is in reverse (for performance reasons).
5304 gboolean with_timestamps;
5306 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5308 twt_udata *user_data = udata;
5309 VikTrackpoint *p1, *p2;
5311 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5315 if (VIK_TRACK(value)->trackpoints) {
5316 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5317 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5319 if ( user_data->with_timestamps ) {
5320 if (!p1->has_timestamp || !p2->has_timestamp) {
5325 // Don't add tracks with timestamps when getting non timestamp tracks
5326 if (p1->has_timestamp || p2->has_timestamp) {
5332 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5335 /* called for each key in track hash table. if original track user_data[1] is close enough
5336 * to the passed one, add it to list in user_data[0]
5338 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5341 VikTrackpoint *p1, *p2;
5342 VikTrack *trk = VIK_TRACK(value);
5344 GList **nearby_tracks = ((gpointer *)user_data)[0];
5345 GList *tpoints = ((gpointer *)user_data)[1];
5348 * detect reasons for not merging, and return
5349 * if no reason is found not to merge, then do it.
5352 // Exclude the original track from the compiled list
5353 if (trk->trackpoints == tpoints) {
5357 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5358 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5360 if (trk->trackpoints) {
5361 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5362 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5364 if (!p1->has_timestamp || !p2->has_timestamp) {
5365 //g_print("no timestamp\n");
5369 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5370 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5371 if (! (abs(t1 - p2->timestamp) < threshold ||
5373 abs(p1->timestamp - t2) < threshold)
5380 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5383 /* comparison function used to sort tracks; a and b are hash table keys */
5384 /* Not actively used - can be restored if needed
5385 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5387 GHashTable *tracks = user_data;
5390 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5391 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5393 if (t1 < t2) return -1;
5394 if (t1 > t2) return 1;
5399 /* comparison function used to sort trackpoints */
5400 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5402 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5404 if (t1 < t2) return -1;
5405 if (t1 > t2) return 1;
5410 * comparison function which can be used to sort tracks or waypoints by name
5412 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5414 const gchar* namea = (const gchar*) a;
5415 const gchar* nameb = (const gchar*) b;
5416 if ( namea == NULL || nameb == NULL)
5419 // Same sort method as used in the vik_treeview_*_alphabetize functions
5420 return strcmp ( namea, nameb );
5424 * Attempt to merge selected track with other tracks specified by the user
5425 * Tracks to merge with must be of the same 'type' as the selected track -
5426 * either all with timestamps, or all without timestamps
5428 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5430 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5431 GList *other_tracks = NULL;
5432 GHashTable *ght_tracks;
5433 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5434 ght_tracks = vtl->routes;
5436 ght_tracks = vtl->tracks;
5438 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5443 if ( !track->trackpoints )
5447 udata.result = &other_tracks;
5448 udata.exclude = track->trackpoints;
5449 // Allow merging with 'similar' time type time tracks
5450 // i.e. either those times, or those without
5451 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5453 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5454 other_tracks = g_list_reverse(other_tracks);
5456 if ( !other_tracks ) {
5457 if ( udata.with_timestamps )
5458 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5460 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5464 // Sort alphabetically for user presentation
5465 // Convert into list of names for usage with dialog function
5466 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5467 GList *other_tracks_names = NULL;
5468 GList *iter = g_list_first ( other_tracks );
5470 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5471 iter = g_list_next ( iter );
5474 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5476 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5480 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5481 g_list_free(other_tracks);
5482 g_list_free(other_tracks_names);
5487 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5488 VikTrack *merge_track;
5489 if ( track->is_route )
5490 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5492 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5495 vik_track_steal_and_append_trackpoints ( track, merge_track );
5496 if ( track->is_route )
5497 vik_trw_layer_delete_route (vtl, merge_track);
5499 vik_trw_layer_delete_track (vtl, merge_track);
5500 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5503 for (l = merge_list; l != NULL; l = g_list_next(l))
5505 g_list_free(merge_list);
5507 vik_layer_emit_update( VIK_LAYER(vtl) );
5511 // c.f. trw_layer_sorted_track_id_by_name_list
5512 // but don't add the specified track to the list (normally current track)
5513 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5515 twt_udata *user_data = udata;
5518 if (trk->trackpoints == user_data->exclude) {
5522 // Sort named list alphabetically
5523 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5527 * Join - this allows combining 'tracks' and 'track routes'
5528 * i.e. doesn't care about whether tracks have consistent timestamps
5529 * ATM can only append one track at a time to the currently selected track
5531 static void trw_layer_append_track ( gpointer pass_along[6] )
5534 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5536 GHashTable *ght_tracks;
5537 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5538 ght_tracks = vtl->routes;
5540 ght_tracks = vtl->tracks;
5542 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5547 GList *other_tracks_names = NULL;
5549 // Sort alphabetically for user presentation
5550 // Convert into list of names for usage with dialog function
5551 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5553 udata.result = &other_tracks_names;
5554 udata.exclude = trk->trackpoints;
5556 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5558 // Note the limit to selecting one track only
5559 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5560 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5561 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5564 trk->is_route ? _("Append Route"): _("Append Track"),
5565 trk->is_route ? _("Select the route to append after the current route") :
5566 _("Select the track to append after the current track") );
5568 g_list_free(other_tracks_names);
5570 // It's a list, but shouldn't contain more than one other track!
5571 if ( append_list ) {
5573 for (l = append_list; l != NULL; l = g_list_next(l)) {
5574 // TODO: at present this uses the first track found by name,
5575 // which with potential multiple same named tracks may not be the one selected...
5576 VikTrack *append_track;
5577 if ( trk->is_route )
5578 append_track = vik_trw_layer_get_route ( vtl, l->data );
5580 append_track = vik_trw_layer_get_track ( vtl, l->data );
5582 if ( append_track ) {
5583 vik_track_steal_and_append_trackpoints ( trk, append_track );
5584 if ( trk->is_route )
5585 vik_trw_layer_delete_route (vtl, append_track);
5587 vik_trw_layer_delete_track (vtl, append_track);
5590 for (l = append_list; l != NULL; l = g_list_next(l))
5592 g_list_free(append_list);
5594 vik_layer_emit_update( VIK_LAYER(vtl) );
5599 * Very similar to trw_layer_append_track for joining
5600 * but this allows selection from the 'other' list
5601 * If a track is selected, then is shows routes and joins the selected one
5602 * If a route is selected, then is shows tracks and joins the selected one
5604 static void trw_layer_append_other ( gpointer pass_along[6] )
5607 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5609 GHashTable *ght_mykind, *ght_others;
5610 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5611 ght_mykind = vtl->routes;
5612 ght_others = vtl->tracks;
5615 ght_mykind = vtl->tracks;
5616 ght_others = vtl->routes;
5619 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5624 GList *other_tracks_names = NULL;
5626 // Sort alphabetically for user presentation
5627 // Convert into list of names for usage with dialog function
5628 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5630 udata.result = &other_tracks_names;
5631 udata.exclude = trk->trackpoints;
5633 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5635 // Note the limit to selecting one track only
5636 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5637 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5638 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5641 trk->is_route ? _("Append Track"): _("Append Route"),
5642 trk->is_route ? _("Select the track to append after the current route") :
5643 _("Select the route to append after the current track") );
5645 g_list_free(other_tracks_names);
5647 // It's a list, but shouldn't contain more than one other track!
5648 if ( append_list ) {
5650 for (l = append_list; l != NULL; l = g_list_next(l)) {
5651 // TODO: at present this uses the first track found by name,
5652 // which with potential multiple same named tracks may not be the one selected...
5654 // Get FROM THE OTHER TYPE list
5655 VikTrack *append_track;
5656 if ( trk->is_route )
5657 append_track = vik_trw_layer_get_track ( vtl, l->data );
5659 append_track = vik_trw_layer_get_route ( vtl, l->data );
5661 if ( append_track ) {
5663 if ( !append_track->is_route &&
5664 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5665 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5667 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5668 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5669 vik_track_merge_segments ( append_track );
5670 vik_track_to_routepoints ( append_track );
5677 vik_track_steal_and_append_trackpoints ( trk, append_track );
5679 // Delete copied which is FROM THE OTHER TYPE list
5680 if ( trk->is_route )
5681 vik_trw_layer_delete_track (vtl, append_track);
5683 vik_trw_layer_delete_route (vtl, append_track);
5686 for (l = append_list; l != NULL; l = g_list_next(l))
5688 g_list_free(append_list);
5689 vik_layer_emit_update( VIK_LAYER(vtl) );
5693 /* merge by segments */
5694 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5696 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5697 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5698 guint segments = vik_track_merge_segments ( trk );
5699 // NB currently no need to redraw as segments not actually shown on the display
5700 // However inform the user of what happened:
5702 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5703 g_snprintf(str, 64, tmp_str, segments);
5704 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5707 /* merge by time routine */
5708 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5710 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5714 GList *tracks_with_timestamp = NULL;
5715 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5716 if (orig_trk->trackpoints &&
5717 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5718 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5723 udata.result = &tracks_with_timestamp;
5724 udata.exclude = orig_trk->trackpoints;
5725 udata.with_timestamps = TRUE;
5726 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5727 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5729 if (!tracks_with_timestamp) {
5730 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5733 g_list_free(tracks_with_timestamp);
5735 static guint threshold_in_minutes = 1;
5736 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5737 _("Merge Threshold..."),
5738 _("Merge when time between tracks less than:"),
5739 &threshold_in_minutes)) {
5743 // keep attempting to merge all tracks until no merges within the time specified is possible
5744 gboolean attempt_merge = TRUE;
5745 GList *nearby_tracks = NULL;
5747 static gpointer params[3];
5749 while ( attempt_merge ) {
5751 // Don't try again unless tracks have changed
5752 attempt_merge = FALSE;
5754 trps = orig_trk->trackpoints;
5758 if (nearby_tracks) {
5759 g_list_free(nearby_tracks);
5760 nearby_tracks = NULL;
5763 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5764 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5766 /* g_print("Original track times: %d and %d\n", t1, t2); */
5767 params[0] = &nearby_tracks;
5768 params[1] = (gpointer)trps;
5769 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5771 /* get a list of adjacent-in-time tracks */
5772 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5775 GList *l = nearby_tracks;
5778 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5779 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5781 t1 = get_first_trackpoint(l)->timestamp;
5782 t2 = get_last_trackpoint(l)->timestamp;
5783 #undef get_first_trackpoint
5784 #undef get_last_trackpoint
5785 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5788 /* remove trackpoints from merged track, delete track */
5789 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5790 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5792 // Tracks have changed, therefore retry again against all the remaining tracks
5793 attempt_merge = TRUE;
5798 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5801 g_list_free(nearby_tracks);
5803 vik_layer_emit_update( VIK_LAYER(vtl) );
5807 * Split a track at the currently selected trackpoint
5809 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5811 if ( !vtl->current_tpl )
5814 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5815 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5817 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5818 GList *newglist = g_list_alloc ();
5819 newglist->prev = NULL;
5820 newglist->next = vtl->current_tpl->next;
5821 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5822 tr->trackpoints = newglist;
5824 vtl->current_tpl->next->prev = newglist; /* end old track here */
5825 vtl->current_tpl->next = NULL;
5827 // Bounds of the selected track changed due to the split
5828 vik_track_calculate_bounds ( vtl->current_tp_track );
5830 vtl->current_tpl = newglist; /* change tp to first of new track. */
5831 vtl->current_tp_track = tr;
5834 vik_trw_layer_add_route ( vtl, name, tr );
5836 vik_trw_layer_add_track ( vtl, name, tr );
5838 // Bounds of the new track created by the split
5839 vik_track_calculate_bounds ( tr );
5845 // Also need id of newly created track
5848 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5850 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5852 if ( trkf && udata.uuid )
5853 vtl->current_tp_id = udata.uuid;
5855 vtl->current_tp_id = NULL;
5857 vik_layer_emit_update(VIK_LAYER(vtl));
5863 /* split by time routine */
5864 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5866 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5867 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5868 GList *trps = track->trackpoints;
5870 GList *newlists = NULL;
5871 GList *newtps = NULL;
5872 static guint thr = 1;
5879 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5880 _("Split Threshold..."),
5881 _("Split when time between trackpoints exceeds:"),
5886 /* iterate through trackpoints, and copy them into new lists without touching original list */
5887 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5891 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5893 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5896 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5897 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5898 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5900 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5905 if (ts - prev_ts > thr*60) {
5906 /* flush accumulated trackpoints into new list */
5907 newlists = g_list_append(newlists, g_list_reverse(newtps));
5911 /* accumulate trackpoint copies in newtps, in reverse order */
5912 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5914 iter = g_list_next(iter);
5917 newlists = g_list_append(newlists, g_list_reverse(newtps));
5920 /* put lists of trackpoints into tracks */
5922 // Only bother updating if the split results in new tracks
5923 if (g_list_length (newlists) > 1) {
5928 tr = vik_track_copy ( track, FALSE );
5929 tr->trackpoints = (GList *)(iter->data);
5931 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5932 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5933 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5934 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5935 g_free ( new_tr_name );
5936 vik_track_calculate_bounds ( tr );
5937 iter = g_list_next(iter);
5939 // Remove original track and then update the display
5940 vik_trw_layer_delete_track (vtl, track);
5941 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5943 g_list_free(newlists);
5947 * Split a track by the number of points as specified by the user
5949 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5951 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5953 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5954 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5956 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5961 // Check valid track
5962 GList *trps = track->trackpoints;
5966 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5967 _("Split Every Nth Point"),
5968 _("Split on every Nth point:"),
5969 250, // Default value as per typical limited track capacity of various GPS devices
5973 // Was a valid number returned?
5979 GList *newlists = NULL;
5980 GList *newtps = NULL;
5985 /* accumulate trackpoint copies in newtps, in reverse order */
5986 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5988 if (count >= points) {
5989 /* flush accumulated trackpoints into new list */
5990 newlists = g_list_append(newlists, g_list_reverse(newtps));
5994 iter = g_list_next(iter);
5997 // If there is a remaining chunk put that into the new split list
5998 // This may well be the whole track if no split points were encountered
6000 newlists = g_list_append(newlists, g_list_reverse(newtps));
6003 /* put lists of trackpoints into tracks */
6005 // Only bother updating if the split results in new tracks
6006 if (g_list_length (newlists) > 1) {
6011 tr = vik_track_copy ( track, FALSE );
6012 tr->trackpoints = (GList *)(iter->data);
6014 if ( track->is_route ) {
6015 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6016 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6019 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6020 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6022 g_free ( new_tr_name );
6023 vik_track_calculate_bounds ( tr );
6025 iter = g_list_next(iter);
6027 // Remove original track and then update the display
6028 if ( track->is_route )
6029 vik_trw_layer_delete_route (vtl, track);
6031 vik_trw_layer_delete_track (vtl, track);
6032 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6034 g_list_free(newlists);
6038 * Split a track at the currently selected trackpoint
6040 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6042 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6043 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6044 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6048 * Split a track by its segments
6049 * Routes do not have segments so don't call this for routes
6051 static void trw_layer_split_segments ( gpointer pass_along[6] )
6053 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6054 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6061 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6064 for ( i = 0; i < ntracks; i++ ) {
6066 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6067 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6068 g_free ( new_tr_name );
6073 // Remove original track
6074 vik_trw_layer_delete_track ( vtl, trk );
6075 vik_layer_emit_update ( VIK_LAYER(vtl) );
6078 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6081 /* end of split/merge routines */
6083 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6087 // Find available adjacent trackpoint
6088 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6089 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6090 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6092 // Delete current trackpoint
6093 vik_trackpoint_free ( vtl->current_tpl->data );
6094 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6096 // Set to current to the available adjacent trackpoint
6097 vtl->current_tpl = new_tpl;
6099 if ( vtl->current_tp_track ) {
6100 vik_track_calculate_bounds ( vtl->current_tp_track );
6104 // Delete current trackpoint
6105 vik_trackpoint_free ( vtl->current_tpl->data );
6106 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6107 trw_layer_cancel_current_tp ( vtl, FALSE );
6112 * Delete the selected point
6114 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6116 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6118 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6119 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6121 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6126 if ( !vtl->current_tpl )
6129 trw_layer_trackpoint_selected_delete ( vtl, trk );
6131 // Track has been updated so update tps:
6132 trw_layer_cancel_tps_of_track ( vtl, trk );
6134 vik_layer_emit_update ( VIK_LAYER(vtl) );
6138 * Delete adjacent track points at the same position
6139 * AKA Delete Dulplicates on the Properties Window
6141 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6143 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6145 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6146 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6148 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6153 gulong removed = vik_track_remove_dup_points ( trk );
6155 // Track has been updated so update tps:
6156 trw_layer_cancel_tps_of_track ( vtl, trk );
6158 // Inform user how much was deleted as it's not obvious from the normal view
6160 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6161 g_snprintf(str, 64, tmp_str, removed);
6162 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6164 vik_layer_emit_update ( VIK_LAYER(vtl) );
6168 * Delete adjacent track points with the same timestamp
6169 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6171 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6173 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6175 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6176 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6178 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6183 gulong removed = vik_track_remove_same_time_points ( trk );
6185 // Track has been updated so update tps:
6186 trw_layer_cancel_tps_of_track ( vtl, trk );
6188 // Inform user how much was deleted as it's not obvious from the normal view
6190 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6191 g_snprintf(str, 64, tmp_str, removed);
6192 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6194 vik_layer_emit_update ( VIK_LAYER(vtl) );
6200 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6202 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6204 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6205 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6207 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6212 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6214 vik_layer_emit_update ( VIK_LAYER(vtl) );
6217 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6219 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6221 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6222 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6224 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6229 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6231 vik_layer_emit_update ( VIK_LAYER(vtl) );
6237 static void trw_layer_reverse ( gpointer pass_along[6] )
6239 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6241 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6242 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6244 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6249 vik_track_reverse ( track );
6251 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6255 * Similar to trw_layer_enum_item, but this uses a sorted method
6258 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6260 GList **list = (GList**)udata;
6261 // *list = g_list_prepend(*all, key); //unsorted method
6262 // Sort named list alphabetically
6263 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6268 * Now Waypoint specific sort
6270 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6272 GList **list = (GList**)udata;
6273 // Sort named list alphabetically
6274 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6278 * Track specific sort
6280 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6282 GList **list = (GList**)udata;
6283 // Sort named list alphabetically
6284 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6289 gboolean has_same_track_name;
6290 const gchar *same_track_name;
6291 } same_track_name_udata;
6293 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6295 const gchar* namea = (const gchar*) aa;
6296 const gchar* nameb = (const gchar*) bb;
6299 gint result = strcmp ( namea, nameb );
6301 if ( result == 0 ) {
6302 // Found two names the same
6303 same_track_name_udata *user_data = udata;
6304 user_data->has_same_track_name = TRUE;
6305 user_data->same_track_name = namea;
6308 // Leave ordering the same
6313 * Find out if any tracks have the same name in this hash table
6315 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6317 // Sort items by name, then compare if any next to each other are the same
6319 GList *track_names = NULL;
6320 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6323 if ( ! track_names )
6326 same_track_name_udata udata;
6327 udata.has_same_track_name = FALSE;
6329 // Use sort routine to traverse list comparing items
6330 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6331 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6332 // Still no tracks...
6336 return udata.has_same_track_name;
6340 * Force unqiue track names for the track table specified
6341 * Note the panel is a required parameter to enable the update of the names displayed
6342 * Specify if on tracks or else on routes
6344 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6346 // . Search list for an instance of repeated name
6347 // . get track of this name
6348 // . create new name
6349 // . rename track & update equiv. treeview iter
6350 // . repeat until all different
6352 same_track_name_udata udata;
6354 GList *track_names = NULL;
6355 udata.has_same_track_name = FALSE;
6356 udata.same_track_name = NULL;
6358 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6361 if ( ! track_names )
6364 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6366 // Still no tracks...
6367 if ( ! dummy_list1 )
6370 while ( udata.has_same_track_name ) {
6372 // Find a track with the same name
6375 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6377 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6381 g_critical("Houston, we've had a problem.");
6382 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6383 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6388 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6389 vik_track_set_name ( trk, newname );
6395 // Need want key of it for treeview update
6396 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6398 if ( trkf && udataU.uuid ) {
6402 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6404 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6407 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6409 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6411 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6415 // Start trying to find same names again...
6417 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6418 udata.has_same_track_name = FALSE;
6419 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6421 // No tracks any more - give up searching
6422 if ( ! dummy_list2 )
6423 udata.has_same_track_name = FALSE;
6427 vik_layers_panel_emit_update ( vlp );
6430 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6432 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6435 switch (GPOINTER_TO_INT (pass_along[2])) {
6436 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6437 iter = &(vtl->tracks_iter);
6438 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6440 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6441 iter = &(vtl->routes_iter);
6442 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6444 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6445 iter = &(vtl->waypoints_iter);
6446 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6450 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6453 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6455 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6458 switch (GPOINTER_TO_INT (pass_along[2])) {
6459 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6460 iter = &(vtl->tracks_iter);
6461 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6463 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6464 iter = &(vtl->routes_iter);
6465 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6467 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6468 iter = &(vtl->waypoints_iter);
6469 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6473 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6479 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6481 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6484 // Ensure list of track names offered is unique
6485 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6486 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6487 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6488 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6494 // Sort list alphabetically for better presentation
6495 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6498 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6502 // Get list of items to delete from the user
6503 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6506 _("Delete Selection"),
6507 _("Select tracks to delete"));
6510 // Delete requested tracks
6511 // since specificly requested, IMHO no need for extra confirmation
6512 if ( delete_list ) {
6514 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6515 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6516 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6518 g_list_free(delete_list);
6519 vik_layer_emit_update( VIK_LAYER(vtl) );
6526 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6528 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6531 // Ensure list of track names offered is unique
6532 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6533 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6534 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6535 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6541 // Sort list alphabetically for better presentation
6542 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6545 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6549 // Get list of items to delete from the user
6550 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6553 _("Delete Selection"),
6554 _("Select routes to delete") );
6557 // Delete requested routes
6558 // since specificly requested, IMHO no need for extra confirmation
6559 if ( delete_list ) {
6561 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6562 // This deletes first route it finds of that name (but uniqueness is enforced above)
6563 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6565 g_list_free(delete_list);
6566 vik_layer_emit_update( VIK_LAYER(vtl) );
6571 gboolean has_same_waypoint_name;
6572 const gchar *same_waypoint_name;
6573 } same_waypoint_name_udata;
6575 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6577 const gchar* namea = (const gchar*) aa;
6578 const gchar* nameb = (const gchar*) bb;
6581 gint result = strcmp ( namea, nameb );
6583 if ( result == 0 ) {
6584 // Found two names the same
6585 same_waypoint_name_udata *user_data = udata;
6586 user_data->has_same_waypoint_name = TRUE;
6587 user_data->same_waypoint_name = namea;
6590 // Leave ordering the same
6595 * Find out if any waypoints have the same name in this layer
6597 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6599 // Sort items by name, then compare if any next to each other are the same
6601 GList *waypoint_names = NULL;
6602 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6605 if ( ! waypoint_names )
6608 same_waypoint_name_udata udata;
6609 udata.has_same_waypoint_name = FALSE;
6611 // Use sort routine to traverse list comparing items
6612 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6613 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6614 // Still no waypoints...
6618 return udata.has_same_waypoint_name;
6622 * Force unqiue waypoint names for this layer
6623 * Note the panel is a required parameter to enable the update of the names displayed
6625 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6627 // . Search list for an instance of repeated name
6628 // . get waypoint of this name
6629 // . create new name
6630 // . rename waypoint & update equiv. treeview iter
6631 // . repeat until all different
6633 same_waypoint_name_udata udata;
6635 GList *waypoint_names = NULL;
6636 udata.has_same_waypoint_name = FALSE;
6637 udata.same_waypoint_name = NULL;
6639 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6642 if ( ! waypoint_names )
6645 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6647 // Still no waypoints...
6648 if ( ! dummy_list1 )
6651 while ( udata.has_same_waypoint_name ) {
6653 // Find a waypoint with the same name
6654 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6658 g_critical("Houston, we've had a problem.");
6659 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6660 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6665 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6667 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6669 // Start trying to find same names again...
6670 waypoint_names = NULL;
6671 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6672 udata.has_same_waypoint_name = FALSE;
6673 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6675 // No waypoints any more - give up searching
6676 if ( ! dummy_list2 )
6677 udata.has_same_waypoint_name = FALSE;
6681 vik_layers_panel_emit_update ( vlp );
6687 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6689 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6692 // Ensure list of waypoint names offered is unique
6693 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6694 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6695 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6696 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6702 // Sort list alphabetically for better presentation
6703 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6705 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6709 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6711 // Get list of items to delete from the user
6712 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6715 _("Delete Selection"),
6716 _("Select waypoints to delete"));
6719 // Delete requested waypoints
6720 // since specificly requested, IMHO no need for extra confirmation
6721 if ( delete_list ) {
6723 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6724 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6725 trw_layer_delete_waypoint_by_name (vtl, l->data);
6727 g_list_free(delete_list);
6729 trw_layer_calculate_bounds_waypoints ( vtl );
6730 vik_layer_emit_update( VIK_LAYER(vtl) );
6738 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6740 vik_treeview_item_toggle_visible ( vt, it );
6746 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6748 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6754 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6756 wp->visible = GPOINTER_TO_INT (on_off);
6762 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6764 wp->visible = !wp->visible;
6770 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6772 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6773 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6774 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6775 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6777 vik_layer_emit_update ( VIK_LAYER(vtl) );
6783 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6785 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6786 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6787 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6788 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6790 vik_layer_emit_update ( VIK_LAYER(vtl) );
6796 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6798 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6799 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6800 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6802 vik_layer_emit_update ( VIK_LAYER(vtl) );
6808 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6810 trk->visible = GPOINTER_TO_INT (on_off);
6816 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6818 trk->visible = !trk->visible;
6824 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6826 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6827 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6828 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6829 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6831 vik_layer_emit_update ( VIK_LAYER(vtl) );
6837 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6839 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6840 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6841 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6842 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6844 vik_layer_emit_update ( VIK_LAYER(vtl) );
6850 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6852 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6853 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6854 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6856 vik_layer_emit_update ( VIK_LAYER(vtl) );
6862 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6864 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6865 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6866 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6867 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6869 vik_layer_emit_update ( VIK_LAYER(vtl) );
6875 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6877 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6878 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6879 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6880 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6882 vik_layer_emit_update ( VIK_LAYER(vtl) );
6888 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6890 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6891 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6892 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6894 vik_layer_emit_update ( VIK_LAYER(vtl) );
6898 * trw_layer_analyse_close:
6900 * Stuff to do on dialog closure
6902 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6904 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6905 gtk_widget_destroy ( dialog );
6906 vtl->tracks_analysis_dialog = NULL;
6910 * vik_trw_layer_build_track_list_t:
6912 * Helper function to construct a list of #vik_trw_track_list_t
6914 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
6916 GList *tracks_and_layers = NULL;
6917 // build tracks_and_layers list
6919 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6920 vtdl->trk = VIK_TRACK(tracks->data);
6922 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6923 tracks = g_list_next ( tracks );
6925 return tracks_and_layers;
6929 * trw_layer_create_track_list:
6931 * Create the latest list of tracks with the associated layer(s)
6932 * Although this will always be from a single layer here
6934 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
6936 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6937 GList *tracks = NULL;
6938 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6939 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6941 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6943 return vik_trw_layer_build_track_list_t ( vtl, tracks );
6946 static void trw_layer_tracks_stats ( gpointer lav[2] )
6948 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6949 // There can only be one!
6950 if ( vtl->tracks_analysis_dialog )
6953 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6954 VIK_LAYER(vtl)->name,
6956 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6957 trw_layer_create_track_list,
6958 trw_layer_analyse_close );
6964 static void trw_layer_routes_stats ( gpointer lav[2] )
6966 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6967 // There can only be one!
6968 if ( vtl->tracks_analysis_dialog )
6971 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6972 VIK_LAYER(vtl)->name,
6974 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6975 trw_layer_create_track_list,
6976 trw_layer_analyse_close );
6979 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6981 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6983 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6986 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6988 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6991 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6992 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6996 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6998 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7001 if ( !strncmp(wp->comment, "http", 4) ) {
7002 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7003 } else if ( !strncmp(wp->description, "http", 4) ) {
7004 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7008 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7010 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7012 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7014 // No actual change to the name supplied
7016 if (strcmp(newname, wp->name) == 0 )
7019 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7022 // An existing waypoint has been found with the requested name
7023 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7024 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7029 // Update WP name and refresh the treeview
7030 vik_waypoint_set_name (wp, newname);
7032 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7033 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7035 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7040 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7042 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7044 // No actual change to the name supplied
7046 if (strcmp(newname, trk->name) == 0)
7049 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7052 // An existing track has been found with the requested name
7053 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7054 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7058 // Update track name and refresh GUI parts
7059 vik_track_set_name (trk, newname);
7061 // Update any subwindows that could be displaying this track which has changed name
7062 // Only one Track Edit Window
7063 if ( l->current_tp_track == trk && l->tpwin ) {
7064 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7066 // Property Dialog of the track
7067 vik_trw_layer_propwin_update ( trk );
7069 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7070 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7072 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7077 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7079 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7081 // No actual change to the name supplied
7083 if (strcmp(newname, trk->name) == 0)
7086 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7089 // An existing track has been found with the requested name
7090 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7091 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7095 // Update track name and refresh GUI parts
7096 vik_track_set_name (trk, newname);
7098 // Update any subwindows that could be displaying this track which has changed name
7099 // Only one Track Edit Window
7100 if ( l->current_tp_track == trk && l->tpwin ) {
7101 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7103 // Property Dialog of the track
7104 vik_trw_layer_propwin_update ( trk );
7106 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7107 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7109 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7116 static gboolean is_valid_geocache_name ( gchar *str )
7118 gint len = strlen ( str );
7119 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]));
7122 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7124 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7125 a_acquire_set_filter_track ( trk );
7128 #ifdef VIK_CONFIG_GOOGLE
7129 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7131 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7132 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7135 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7137 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7139 gchar *escaped = uri_escape ( tr->comment );
7140 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7141 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7148 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7149 /* viewpoint is now available instead */
7150 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7152 static gpointer pass_along[8];
7154 gboolean rv = FALSE;
7157 pass_along[1] = vlp;
7158 pass_along[2] = GINT_TO_POINTER (subtype);
7159 pass_along[3] = sublayer;
7160 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7161 pass_along[5] = vvp;
7162 pass_along[6] = iter;
7163 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7165 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7169 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7171 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7172 gtk_widget_show ( item );
7174 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7175 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7176 if (tr && tr->property_dialog)
7177 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7179 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7180 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7181 if (tr && tr->property_dialog)
7182 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7185 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7187 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7188 gtk_widget_show ( item );
7190 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7191 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7192 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7193 gtk_widget_show ( item );
7195 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7196 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7197 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7198 gtk_widget_show ( item );
7200 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7202 gboolean separator_created = FALSE;
7204 /* could be a right-click using the tool */
7205 if ( vlp != NULL ) {
7206 item = gtk_menu_item_new ();
7207 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7208 gtk_widget_show ( item );
7210 separator_created = TRUE;
7212 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7213 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7214 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7215 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7216 gtk_widget_show ( item );
7219 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7221 if ( wp && wp->name ) {
7222 if ( is_valid_geocache_name ( wp->name ) ) {
7224 if ( !separator_created ) {
7225 item = gtk_menu_item_new ();
7226 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7227 gtk_widget_show ( item );
7228 separator_created = TRUE;
7231 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7232 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7233 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7234 gtk_widget_show ( item );
7238 if ( wp && wp->image )
7240 if ( !separator_created ) {
7241 item = gtk_menu_item_new ();
7242 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7243 gtk_widget_show ( item );
7244 separator_created = TRUE;
7247 // Set up image paramater
7248 pass_along[5] = wp->image;
7250 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7251 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
7252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7253 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7254 gtk_widget_show ( item );
7256 #ifdef VIK_CONFIG_GEOTAG
7257 GtkWidget *geotag_submenu = gtk_menu_new ();
7258 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7260 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7261 gtk_widget_show ( item );
7262 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7264 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7266 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7267 gtk_widget_show ( item );
7269 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7271 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7272 gtk_widget_show ( item );
7278 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7279 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7280 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7281 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7283 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7284 gtk_widget_show ( item );
7291 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7292 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7294 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7295 gtk_widget_show ( item );
7296 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7297 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7298 gtk_widget_set_sensitive ( item, TRUE );
7300 gtk_widget_set_sensitive ( item, FALSE );
7303 item = gtk_menu_item_new ();
7304 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7305 gtk_widget_show ( item );
7308 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7311 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7312 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7314 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7315 gtk_widget_show ( item );
7318 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7320 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7321 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7323 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7324 gtk_widget_show ( item );
7326 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7327 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7329 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7330 gtk_widget_show ( item );
7332 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7336 gtk_widget_show ( item );
7338 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7339 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7340 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7341 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7342 gtk_widget_show ( item );
7344 GtkWidget *vis_submenu = gtk_menu_new ();
7345 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7346 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7347 gtk_widget_show ( item );
7348 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7350 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7351 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7353 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7354 gtk_widget_show ( item );
7356 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7357 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7358 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7359 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7360 gtk_widget_show ( item );
7362 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7363 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7365 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7366 gtk_widget_show ( item );
7369 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7373 if ( l->current_track && !l->current_track->is_route ) {
7374 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7375 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7376 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7377 gtk_widget_show ( item );
7379 item = gtk_menu_item_new ();
7380 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7381 gtk_widget_show ( item );
7384 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7385 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7386 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7387 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7388 gtk_widget_show ( item );
7390 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7391 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7392 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7393 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7394 gtk_widget_show ( item );
7395 // Make it available only when a new track *not* already in progress
7396 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7398 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7399 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7400 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7401 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7402 gtk_widget_show ( item );
7404 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7405 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7407 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7408 gtk_widget_show ( item );
7410 GtkWidget *vis_submenu = gtk_menu_new ();
7411 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7412 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7413 gtk_widget_show ( item );
7414 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7416 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7417 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7418 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7419 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7420 gtk_widget_show ( item );
7422 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7423 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7424 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7425 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7426 gtk_widget_show ( item );
7428 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7429 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7430 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7431 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7432 gtk_widget_show ( item );
7434 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7436 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7437 gtk_widget_show ( item );
7440 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7444 if ( l->current_track && l->current_track->is_route ) {
7445 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7446 // Reuse finish track method
7447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7448 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7449 gtk_widget_show ( item );
7451 item = gtk_menu_item_new ();
7452 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7453 gtk_widget_show ( item );
7456 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7457 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7458 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7459 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7460 gtk_widget_show ( item );
7462 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7463 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7464 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7465 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7466 gtk_widget_show ( item );
7467 // Make it available only when a new track *not* already in progress
7468 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7470 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7471 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7473 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7474 gtk_widget_show ( item );
7476 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7477 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7479 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7480 gtk_widget_show ( item );
7482 GtkWidget *vis_submenu = gtk_menu_new ();
7483 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7484 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7485 gtk_widget_show ( item );
7486 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7488 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7489 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7490 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7491 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7492 gtk_widget_show ( item );
7494 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7495 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7496 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7497 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7498 gtk_widget_show ( item );
7500 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7501 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7502 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7503 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7504 gtk_widget_show ( item );
7506 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7507 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7508 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7509 gtk_widget_show ( item );
7513 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7514 GtkWidget *submenu_sort = gtk_menu_new ();
7515 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7516 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7517 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7518 gtk_widget_show ( item );
7519 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7521 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7522 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7524 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7525 gtk_widget_show ( item );
7527 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7528 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7530 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7531 gtk_widget_show ( item );
7534 GtkWidget *upload_submenu = gtk_menu_new ();
7536 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7538 item = gtk_menu_item_new ();
7539 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7540 gtk_widget_show ( item );
7542 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7543 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7544 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7545 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7546 if ( l->current_track ) {
7547 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7548 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7549 gtk_widget_show ( item );
7552 item = gtk_menu_item_new ();
7553 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7554 gtk_widget_show ( item );
7557 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7558 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7560 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7561 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7562 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7563 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7564 gtk_widget_show ( item );
7566 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7568 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7569 gtk_widget_show ( item );
7571 GtkWidget *goto_submenu;
7572 goto_submenu = gtk_menu_new ();
7573 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7574 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7575 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7576 gtk_widget_show ( item );
7577 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7579 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7580 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7581 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7582 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7583 gtk_widget_show ( item );
7585 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7586 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7588 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7589 gtk_widget_show ( item );
7591 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7592 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7594 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7595 gtk_widget_show ( item );
7597 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7598 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7600 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7601 gtk_widget_show ( item );
7603 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7604 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7605 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7606 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7607 gtk_widget_show ( item );
7609 // Routes don't have speeds
7610 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7611 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7612 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7614 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7615 gtk_widget_show ( item );
7618 GtkWidget *combine_submenu;
7619 combine_submenu = gtk_menu_new ();
7620 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7622 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7623 gtk_widget_show ( item );
7624 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7626 // Routes don't have times or segments...
7627 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7628 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7629 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7630 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7631 gtk_widget_show ( item );
7633 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7634 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7635 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7636 gtk_widget_show ( item );
7639 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7640 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7641 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7642 gtk_widget_show ( item );
7644 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7645 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7647 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7648 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7649 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7650 gtk_widget_show ( item );
7652 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7653 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7655 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7657 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7658 gtk_widget_show ( item );
7660 GtkWidget *split_submenu;
7661 split_submenu = gtk_menu_new ();
7662 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7663 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7664 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7665 gtk_widget_show ( item );
7666 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7668 // Routes don't have times or segments...
7669 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7670 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7672 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7673 gtk_widget_show ( item );
7675 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7676 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7677 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7678 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7679 gtk_widget_show ( item );
7682 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7683 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7684 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7685 gtk_widget_show ( item );
7687 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7688 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7689 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7690 gtk_widget_show ( item );
7691 // Make it available only when a trackpoint is selected.
7692 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7694 GtkWidget *insert_submenu = gtk_menu_new ();
7695 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7696 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7697 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7698 gtk_widget_show ( item );
7699 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7701 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7703 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7704 gtk_widget_show ( item );
7705 // Make it available only when a point is selected
7706 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7708 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7710 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7711 gtk_widget_show ( item );
7712 // Make it available only when a point is selected
7713 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7715 GtkWidget *delete_submenu;
7716 delete_submenu = gtk_menu_new ();
7717 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720 gtk_widget_show ( item );
7721 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7723 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7725 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7726 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7727 gtk_widget_show ( item );
7728 // Make it available only when a point is selected
7729 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7731 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7733 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7734 gtk_widget_show ( item );
7736 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7739 gtk_widget_show ( item );
7741 GtkWidget *transform_submenu;
7742 transform_submenu = gtk_menu_new ();
7743 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7744 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7745 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7746 gtk_widget_show ( item );
7747 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7749 GtkWidget *dem_submenu;
7750 dem_submenu = gtk_menu_new ();
7751 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7752 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
7753 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7754 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7756 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7757 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7758 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7759 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7760 gtk_widget_show ( item );
7762 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7763 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7764 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7765 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7766 gtk_widget_show ( item );
7768 GtkWidget *smooth_submenu;
7769 smooth_submenu = gtk_menu_new ();
7770 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7771 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7772 gtk_widget_show ( item );
7773 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7775 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7777 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7778 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7779 gtk_widget_show ( item );
7781 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7782 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7783 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7784 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7785 gtk_widget_show ( item );
7787 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7788 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7790 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7791 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7792 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7793 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7794 gtk_widget_show ( item );
7796 // Routes don't have timestamps - so this is only available for tracks
7797 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7798 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7801 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7802 gtk_widget_show ( item );
7805 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7806 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7808 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7811 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7812 gtk_widget_show ( item );
7814 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7815 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7816 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7817 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7818 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7819 gtk_widget_show ( item );
7822 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7824 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7825 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7827 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7828 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
7829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7830 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7831 gtk_widget_show ( item );
7834 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7835 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7837 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7838 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7839 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7840 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7841 gtk_widget_show ( item );
7843 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7844 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7846 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7847 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7848 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7849 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7850 gtk_widget_show ( item );
7852 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7854 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
7855 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7856 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7857 gtk_widget_show ( item );
7860 // ATM can't upload a single waypoint but can do waypoints to a GPS
7861 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7862 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7863 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7864 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7865 gtk_widget_show ( item );
7866 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7868 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7869 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7870 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7871 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7872 gtk_widget_show ( item );
7876 #ifdef VIK_CONFIG_GOOGLE
7877 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7879 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7880 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7882 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7883 gtk_widget_show ( item );
7887 // Some things aren't usable with routes
7888 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7889 #ifdef VIK_CONFIG_OPENSTREETMAP
7890 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7891 // Convert internal pointer into actual track for usage outside this file
7892 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7893 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7894 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7895 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7896 gtk_widget_show ( item );
7899 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7902 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7903 gtk_widget_show ( item );
7905 /* ATM This function is only available via the layers panel, due to needing a vlp */
7907 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7908 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7909 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7911 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7912 gtk_widget_show ( item );
7916 #ifdef VIK_CONFIG_GEOTAG
7917 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7918 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7919 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7920 gtk_widget_show ( item );
7924 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7925 // Only show on viewport popmenu when a trackpoint is selected
7926 if ( ! vlp && l->current_tpl ) {
7928 item = gtk_menu_item_new ();
7929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7930 gtk_widget_show ( item );
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7933 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7934 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7935 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7936 gtk_widget_show ( item );
7940 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7941 GtkWidget *transform_submenu;
7942 transform_submenu = gtk_menu_new ();
7943 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7944 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7945 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7946 gtk_widget_show ( item );
7947 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7949 GtkWidget *dem_submenu;
7950 dem_submenu = gtk_menu_new ();
7951 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7952 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
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7954 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7956 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
7958 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7959 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7960 gtk_widget_show ( item );
7962 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
7964 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7965 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7966 gtk_widget_show ( item );
7969 gtk_widget_show_all ( GTK_WIDGET(menu) );
7974 // TODO: Probably better to rework this track manipulation in viktrack.c
7975 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
7978 if (!vtl->current_tpl)
7981 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7982 VikTrackpoint *tp_other = NULL;
7985 if (!vtl->current_tpl->prev)
7987 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
7989 if (!vtl->current_tpl->next)
7991 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7994 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
7997 VikTrackpoint *tp_new = vik_trackpoint_new();
7998 struct LatLon ll_current, ll_other;
7999 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8000 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8002 /* main positional interpolation */
8003 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8004 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8006 /* Now other properties that can be interpolated */
8007 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8009 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8010 /* Note here the division is applied to each part, then added
8011 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8012 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8013 tp_new->has_timestamp = TRUE;
8016 if (tp_current->speed != NAN && tp_other->speed != NAN)
8017 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8019 /* TODO - improve interpolation of course, as it may not be correct.
8020 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8021 [similar applies if value is in radians] */
8022 if (tp_current->course != NAN && tp_other->course != NAN)
8023 tp_new->course = (tp_current->course + tp_other->course)/2;
8025 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8027 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8028 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8030 // Otherwise try routes
8031 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8035 gint index = g_list_index ( trk->trackpoints, tp_current );
8039 // NB no recalculation of bounds since it is inserted between points
8040 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8045 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8051 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8055 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8057 if ( vtl->current_tpl )
8059 vtl->current_tpl = NULL;
8060 vtl->current_tp_track = NULL;
8061 vtl->current_tp_id = NULL;
8062 vik_layer_emit_update(VIK_LAYER(vtl));
8066 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8068 g_assert ( vtl->tpwin != NULL );
8069 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8070 trw_layer_cancel_current_tp ( vtl, TRUE );
8072 if ( vtl->current_tpl == NULL )
8075 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8077 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8078 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8080 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8082 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8084 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8088 trw_layer_trackpoint_selected_delete ( vtl, tr );
8090 if ( vtl->current_tpl )
8091 // Reset dialog with the available adjacent trackpoint
8092 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8094 vik_layer_emit_update(VIK_LAYER(vtl));
8096 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8098 if ( vtl->current_tp_track )
8099 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8100 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8102 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8104 if ( vtl->current_tp_track )
8105 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8106 vik_layer_emit_update(VIK_LAYER(vtl));
8108 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8110 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8111 vik_layer_emit_update(VIK_LAYER(vtl));
8113 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8114 vik_layer_emit_update(VIK_LAYER(vtl));
8118 * trw_layer_dialog_shift:
8119 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8121 * Try to reposition a dialog if it's over the specified coord
8122 * so to not obscure the item of interest
8124 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8126 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8128 // Attempt force dialog to be shown so we can find out where it is more reliably...
8129 while ( gtk_events_pending() )
8130 gtk_main_iteration ();
8132 // get parent window position & size
8133 gint win_pos_x, win_pos_y;
8134 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8136 gint win_size_x, win_size_y;
8137 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8139 // get own dialog size
8140 gint dia_size_x, dia_size_y;
8141 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8143 // get own dialog position
8144 gint dia_pos_x, dia_pos_y;
8145 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8147 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8148 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8150 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8152 gint vp_xx, vp_yy; // In viewport pixels
8153 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8155 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8159 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8161 // Transform Viewport pixels into absolute pixels
8162 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8163 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8165 // Is dialog over the point (to within an ^^ edge value)
8166 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8167 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8171 gint hh = vik_viewport_get_height ( vvp );
8173 // Consider the difference in viewport to the full window
8174 gint offset_y = dest_y;
8175 // Add difference between dialog and window sizes
8176 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8178 if ( vp_yy > hh/2 ) {
8179 // Point in bottom half, move window to top half
8180 gtk_window_move ( dialog, dia_pos_x, offset_y );
8183 // Point in top half, move dialog down
8184 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8188 // Shift left<->right
8189 gint ww = vik_viewport_get_width ( vvp );
8191 // Consider the difference in viewport to the full window
8192 gint offset_x = dest_x;
8193 // Add difference between dialog and window sizes
8194 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8196 if ( vp_xx > ww/2 ) {
8197 // Point on right, move window to left
8198 gtk_window_move ( dialog, offset_x, dia_pos_y );
8201 // Point on left, move right
8202 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8210 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8214 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8215 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8216 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8217 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8219 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8221 if ( vtl->current_tpl ) {
8222 // get tp pixel position
8223 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8225 // Shift up<->down to try not to obscure the trackpoint.
8226 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8230 if ( vtl->current_tpl )
8231 if ( vtl->current_tp_track )
8232 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8233 /* set layer name and TP data */
8236 /***************************************************************************
8238 ***************************************************************************/
8240 /*** Utility data structures and functions ****/
8244 gint closest_x, closest_y;
8245 gboolean draw_images;
8246 gpointer *closest_wp_id;
8247 VikWaypoint *closest_wp;
8253 gint closest_x, closest_y;
8254 gpointer closest_track_id;
8255 VikTrackpoint *closest_tp;
8261 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8267 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8269 // If waypoint has an image then use the image size to select
8270 if ( params->draw_images && wp->image ) {
8271 gint slackx, slacky;
8272 slackx = wp->image_width / 2;
8273 slacky = wp->image_height / 2;
8275 if ( x <= params->x + slackx && x >= params->x - slackx
8276 && y <= params->y + slacky && y >= params->y - slacky ) {
8277 params->closest_wp_id = id;
8278 params->closest_wp = wp;
8279 params->closest_x = x;
8280 params->closest_y = y;
8283 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8284 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8285 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8287 params->closest_wp_id = id;
8288 params->closest_wp = wp;
8289 params->closest_x = x;
8290 params->closest_y = y;
8294 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8296 GList *tpl = t->trackpoints;
8302 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8308 tp = VIK_TRACKPOINT(tpl->data);
8310 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8312 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8313 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8314 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8316 params->closest_track_id = id;
8317 params->closest_tp = tp;
8318 params->closest_tpl = tpl;
8319 params->closest_x = x;
8320 params->closest_y = y;
8326 // ATM: Leave this as 'Track' only.
8327 // Not overly bothered about having a snap to route trackpoint capability
8328 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8330 TPSearchParams params;
8334 params.closest_track_id = NULL;
8335 params.closest_tp = NULL;
8336 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8337 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8338 return params.closest_tp;
8341 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8343 WPSearchParams params;
8347 params.draw_images = vtl->drawimages;
8348 params.closest_wp = NULL;
8349 params.closest_wp_id = NULL;
8350 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8351 return params.closest_wp;
8355 // Some forward declarations
8356 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8357 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8358 static void marker_end_move ( tool_ed_t *t );
8361 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8365 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8367 // Here always allow snapping back to the original location
8368 // this is useful when one decides not to move the thing afterall
8369 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8372 if ( event->state & GDK_CONTROL_MASK )
8374 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8376 new_coord = tp->coord;
8380 if ( event->state & GDK_SHIFT_MASK )
8382 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8384 new_coord = wp->coord;
8388 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8390 marker_moveto ( t, x, y );
8397 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8399 if ( t->holding && event->button == 1 )
8402 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8405 if ( event->state & GDK_CONTROL_MASK )
8407 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8409 new_coord = tp->coord;
8413 if ( event->state & GDK_SHIFT_MASK )
8415 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8417 new_coord = wp->coord;
8420 marker_end_move ( t );
8422 // Determine if working on a waypoint or a trackpoint
8423 if ( t->is_waypoint ) {
8424 // Update waypoint position
8425 vtl->current_wp->coord = new_coord;
8426 trw_layer_calculate_bounds_waypoints ( vtl );
8427 // Reset waypoint pointer
8428 vtl->current_wp = NULL;
8429 vtl->current_wp_id = NULL;
8432 if ( vtl->current_tpl ) {
8433 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8435 if ( vtl->current_tp_track )
8436 vik_track_calculate_bounds ( vtl->current_tp_track );
8439 if ( vtl->current_tp_track )
8440 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8441 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8445 vik_layer_emit_update ( VIK_LAYER(vtl) );
8452 Returns true if a waypoint or track is found near the requested event position for this particular layer
8453 The item found is automatically selected
8454 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8456 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8458 if ( event->button != 1 )
8461 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8464 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8468 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8470 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8472 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8473 WPSearchParams wp_params;
8474 wp_params.vvp = vvp;
8475 wp_params.x = event->x;
8476 wp_params.y = event->y;
8477 wp_params.draw_images = vtl->drawimages;
8478 wp_params.closest_wp_id = NULL;
8479 wp_params.closest_wp = NULL;
8481 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8483 if ( wp_params.closest_wp ) {
8486 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8488 // Too easy to move it so must be holding shift to start immediately moving it
8489 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8490 if ( event->state & GDK_SHIFT_MASK ||
8491 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8492 // Put into 'move buffer'
8493 // NB vvp & vw already set in tet
8494 tet->vtl = (gpointer)vtl;
8495 tet->is_waypoint = TRUE;
8497 marker_begin_move (tet, event->x, event->y);
8500 vtl->current_wp = wp_params.closest_wp;
8501 vtl->current_wp_id = wp_params.closest_wp_id;
8503 vik_layer_emit_update ( VIK_LAYER(vtl) );
8509 // Used for both track and route lists
8510 TPSearchParams tp_params;
8511 tp_params.vvp = vvp;
8512 tp_params.x = event->x;
8513 tp_params.y = event->y;
8514 tp_params.closest_track_id = NULL;
8515 tp_params.closest_tp = NULL;
8516 tp_params.closest_tpl = NULL;
8517 tp_params.bbox = bbox;
8519 if (vtl->tracks_visible) {
8520 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8522 if ( tp_params.closest_tp ) {
8524 // Always select + highlight the track
8525 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8527 tet->is_waypoint = FALSE;
8529 // Select the Trackpoint
8530 // Can move it immediately when control held or it's the previously selected tp
8531 if ( event->state & GDK_CONTROL_MASK ||
8532 vtl->current_tpl == tp_params.closest_tpl ) {
8533 // Put into 'move buffer'
8534 // NB vvp & vw already set in tet
8535 tet->vtl = (gpointer)vtl;
8536 marker_begin_move (tet, event->x, event->y);
8539 vtl->current_tpl = tp_params.closest_tpl;
8540 vtl->current_tp_id = tp_params.closest_track_id;
8541 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8543 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8546 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8548 vik_layer_emit_update ( VIK_LAYER(vtl) );
8553 // Try again for routes
8554 if (vtl->routes_visible) {
8555 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8557 if ( tp_params.closest_tp ) {
8559 // Always select + highlight the track
8560 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8562 tet->is_waypoint = FALSE;
8564 // Select the Trackpoint
8565 // Can move it immediately when control held or it's the previously selected tp
8566 if ( event->state & GDK_CONTROL_MASK ||
8567 vtl->current_tpl == tp_params.closest_tpl ) {
8568 // Put into 'move buffer'
8569 // NB vvp & vw already set in tet
8570 tet->vtl = (gpointer)vtl;
8571 marker_begin_move (tet, event->x, event->y);
8574 vtl->current_tpl = tp_params.closest_tpl;
8575 vtl->current_tp_id = tp_params.closest_track_id;
8576 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8578 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8581 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8583 vik_layer_emit_update ( VIK_LAYER(vtl) );
8588 /* these aren't the droids you're looking for */
8589 vtl->current_wp = NULL;
8590 vtl->current_wp_id = NULL;
8591 trw_layer_cancel_current_tp ( vtl, FALSE );
8594 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8599 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8601 if ( event->button != 3 )
8604 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8607 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8610 /* Post menu for the currently selected item */
8612 /* See if a track is selected */
8613 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8614 if ( track && track->visible ) {
8616 if ( track->name ) {
8618 if ( vtl->track_right_click_menu )
8619 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8621 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8628 if ( track->is_route )
8629 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8631 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8633 if ( trkf && udataU.uuid ) {
8636 if ( track->is_route )
8637 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8639 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8641 trw_layer_sublayer_add_menu_items ( vtl,
8642 vtl->track_right_click_menu,
8644 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8650 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8656 /* See if a waypoint is selected */
8657 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8658 if ( waypoint && waypoint->visible ) {
8659 if ( waypoint->name ) {
8661 if ( vtl->wp_right_click_menu )
8662 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8664 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8667 udata.wp = waypoint;
8670 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8672 if ( wpf && udata.uuid ) {
8673 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8675 trw_layer_sublayer_add_menu_items ( vtl,
8676 vtl->wp_right_click_menu,
8678 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8683 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8692 /* background drawing hook, to be passed the viewport */
8693 static gboolean tool_sync_done = TRUE;
8695 static gboolean tool_sync(gpointer data)
8697 VikViewport *vvp = data;
8698 gdk_threads_enter();
8699 vik_viewport_sync(vvp);
8700 tool_sync_done = TRUE;
8701 gdk_threads_leave();
8705 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8708 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8709 gdk_gc_set_function ( t->gc, GDK_INVERT );
8710 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8711 vik_viewport_sync(t->vvp);
8716 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8718 VikViewport *vvp = t->vvp;
8719 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8720 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8724 if (tool_sync_done) {
8725 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8726 tool_sync_done = FALSE;
8730 static void marker_end_move ( tool_ed_t *t )
8732 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8733 g_object_unref ( t->gc );
8737 /*** Edit waypoint ****/
8739 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8741 tool_ed_t *t = g_new(tool_ed_t, 1);
8747 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8752 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8754 WPSearchParams params;
8755 tool_ed_t *t = data;
8756 VikViewport *vvp = t->vvp;
8758 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8765 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8768 if ( vtl->current_wp && vtl->current_wp->visible )
8770 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8772 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8774 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8775 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8777 if ( event->button == 3 )
8778 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8780 marker_begin_move(t, event->x, event->y);
8787 params.x = event->x;
8788 params.y = event->y;
8789 params.draw_images = vtl->drawimages;
8790 params.closest_wp_id = NULL;
8791 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8792 params.closest_wp = NULL;
8793 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8794 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8796 // how do we get here?
8797 marker_begin_move(t, event->x, event->y);
8798 g_critical("shouldn't be here");
8801 else if ( params.closest_wp )
8803 if ( event->button == 3 )
8804 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8806 vtl->waypoint_rightclick = FALSE;
8808 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8810 vtl->current_wp = params.closest_wp;
8811 vtl->current_wp_id = params.closest_wp_id;
8813 /* could make it so don't update if old WP is off screen and new is null but oh well */
8814 vik_layer_emit_update ( VIK_LAYER(vtl) );
8818 vtl->current_wp = NULL;
8819 vtl->current_wp_id = NULL;
8820 vtl->waypoint_rightclick = FALSE;
8821 vik_layer_emit_update ( VIK_LAYER(vtl) );
8825 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8827 tool_ed_t *t = data;
8828 VikViewport *vvp = t->vvp;
8830 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8835 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8838 if ( event->state & GDK_CONTROL_MASK )
8840 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8842 new_coord = tp->coord;
8846 if ( event->state & GDK_SHIFT_MASK )
8848 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8849 if ( wp && wp != vtl->current_wp )
8850 new_coord = wp->coord;
8855 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8857 marker_moveto ( t, x, y );
8864 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8866 tool_ed_t *t = data;
8867 VikViewport *vvp = t->vvp;
8869 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8872 if ( t->holding && event->button == 1 )
8875 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8878 if ( event->state & GDK_CONTROL_MASK )
8880 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8882 new_coord = tp->coord;
8886 if ( event->state & GDK_SHIFT_MASK )
8888 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8889 if ( wp && wp != vtl->current_wp )
8890 new_coord = wp->coord;
8893 marker_end_move ( t );
8895 vtl->current_wp->coord = new_coord;
8897 trw_layer_calculate_bounds_waypoints ( vtl );
8898 vik_layer_emit_update ( VIK_LAYER(vtl) );
8901 /* PUT IN RIGHT PLACE!!! */
8902 if ( event->button == 3 && vtl->waypoint_rightclick )
8904 if ( vtl->wp_right_click_menu )
8905 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8906 if ( vtl->current_wp ) {
8907 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8908 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 );
8909 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8911 vtl->waypoint_rightclick = FALSE;
8916 /*** New track ****/
8918 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8925 GdkDrawable *drawable;
8931 * Draw specified pixmap
8933 static gboolean draw_sync ( gpointer data )
8935 draw_sync_t *ds = (draw_sync_t*) data;
8936 // Sometimes don't want to draw
8937 // normally because another update has taken precedent such as panning the display
8938 // which means this pixmap is no longer valid
8939 if ( ds->vtl->draw_sync_do ) {
8940 gdk_threads_enter();
8941 gdk_draw_drawable (ds->drawable,
8944 0, 0, 0, 0, -1, -1);
8945 ds->vtl->draw_sync_done = TRUE;
8946 gdk_threads_leave();
8952 static gchar* distance_string (gdouble distance)
8956 /* draw label with distance */
8957 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8958 switch (dist_units) {
8959 case VIK_UNITS_DISTANCE_MILES:
8960 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8961 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8962 } else if (distance < 1609.4) {
8963 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8965 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8969 // VIK_UNITS_DISTANCE_KILOMETRES
8970 if (distance >= 1000 && distance < 100000) {
8971 g_sprintf(str, "%3.2f km", distance/1000.0);
8972 } else if (distance < 1000) {
8973 g_sprintf(str, "%d m", (int)distance);
8975 g_sprintf(str, "%d km", (int)distance/1000);
8979 return g_strdup (str);
8983 * Actually set the message in statusbar
8985 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8987 // Only show elevation data when track has some elevation properties
8988 gchar str_gain_loss[64];
8989 str_gain_loss[0] = '\0';
8990 gchar str_last_step[64];
8991 str_last_step[0] = '\0';
8992 gchar *str_total = distance_string (distance);
8994 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8995 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8996 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8998 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9001 if ( last_step > 0 ) {
9002 gchar *tmp = distance_string (last_step);
9003 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9007 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9009 // Write with full gain/loss information
9010 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9011 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9013 g_free ( str_total );
9017 * Figure out what information should be set in the statusbar and then write it
9019 static void update_statusbar ( VikTrwLayer *vtl )
9021 // Get elevation data
9022 gdouble elev_gain, elev_loss;
9023 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9025 /* Find out actual distance of current track */
9026 gdouble distance = vik_track_get_length (vtl->current_track);
9028 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9032 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9034 /* if we haven't sync'ed yet, we don't have time to do more. */
9035 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9036 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9037 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9039 static GdkPixmap *pixmap = NULL;
9041 // Need to check in case window has been resized
9042 w1 = vik_viewport_get_width(vvp);
9043 h1 = vik_viewport_get_height(vvp);
9045 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9047 gdk_drawable_get_size (pixmap, &w2, &h2);
9048 if (w1 != w2 || h1 != h2) {
9049 g_object_unref ( G_OBJECT ( pixmap ) );
9050 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9053 // Reset to background
9054 gdk_draw_drawable (pixmap,
9055 vtl->current_track_newpoint_gc,
9056 vik_viewport_get_pixmap(vvp),
9057 0, 0, 0, 0, -1, -1);
9059 draw_sync_t *passalong;
9062 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9064 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9065 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9066 // thus when we come to reset to the background it would include what we have already drawn!!
9067 gdk_draw_line ( pixmap,
9068 vtl->current_track_newpoint_gc,
9069 x1, y1, event->x, event->y );
9070 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9072 /* Find out actual distance of current track */
9073 gdouble distance = vik_track_get_length (vtl->current_track);
9075 // Now add distance to where the pointer is //
9078 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9079 vik_coord_to_latlon ( &coord, &ll );
9080 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9081 distance = distance + last_step;
9083 // Get elevation data
9084 gdouble elev_gain, elev_loss;
9085 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9087 // Adjust elevation data (if available) for the current pointer position
9089 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9090 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9091 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9092 // Adjust elevation of last track point
9093 if ( elev_new > last_tpt->altitude )
9095 elev_gain += elev_new - last_tpt->altitude;
9098 elev_loss += last_tpt->altitude - elev_new;
9103 // Display of the distance 'tooltip' during track creation is controlled by a preference
9105 if ( a_vik_get_create_track_tooltip() ) {
9107 gchar *str = distance_string (distance);
9109 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9110 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9111 pango_layout_set_text (pl, str, -1);
9113 pango_layout_get_pixel_size ( pl, &wd, &hd );
9116 // offset from cursor a bit depending on font size
9120 // Create a background block to make the text easier to read over the background map
9121 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9122 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9123 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9125 g_object_unref ( G_OBJECT ( pl ) );
9126 g_object_unref ( G_OBJECT ( background_block_gc ) );
9130 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9131 passalong->vtl = vtl;
9132 passalong->pixmap = pixmap;
9133 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9134 passalong->gc = vtl->current_track_newpoint_gc;
9138 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9140 // Update statusbar with full gain/loss information
9141 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9143 // draw pixmap when we have time to
9144 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9145 vtl->draw_sync_done = FALSE;
9146 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9148 return VIK_LAYER_TOOL_ACK;
9151 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9153 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9154 vtl->current_track = NULL;
9155 vik_layer_emit_update ( VIK_LAYER(vtl) );
9157 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9159 if ( vtl->current_track->trackpoints )
9161 GList *last = g_list_last(vtl->current_track->trackpoints);
9162 g_free ( last->data );
9163 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9166 update_statusbar ( vtl );
9168 vik_layer_emit_update ( VIK_LAYER(vtl) );
9175 * Common function to handle trackpoint button requests on either a route or a track
9176 * . enables adding a point via normal click
9177 * . enables removal of last point via right click
9178 * . finishing of the track or route via double clicking
9180 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9184 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9187 if ( event->button == 2 ) {
9188 // As the display is panning, the new track pixmap is now invalid so don't draw it
9189 // otherwise this drawing done results in flickering back to an old image
9190 vtl->draw_sync_do = FALSE;
9194 if ( event->button == 3 )
9196 if ( !vtl->current_track )
9199 if ( vtl->current_track->trackpoints )
9201 GList *last = g_list_last(vtl->current_track->trackpoints);
9202 g_free ( last->data );
9203 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9205 vik_track_calculate_bounds ( vtl->current_track );
9206 update_statusbar ( vtl );
9208 vik_layer_emit_update ( VIK_LAYER(vtl) );
9212 if ( event->type == GDK_2BUTTON_PRESS )
9214 /* subtract last (duplicate from double click) tp then end */
9215 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9217 GList *last = g_list_last(vtl->current_track->trackpoints);
9218 g_free ( last->data );
9219 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9220 /* undo last, then end */
9221 vtl->current_track = NULL;
9223 vik_layer_emit_update ( VIK_LAYER(vtl) );
9227 tp = vik_trackpoint_new();
9228 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9230 /* snap to other TP */
9231 if ( event->state & GDK_CONTROL_MASK )
9233 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9235 tp->coord = other_tp->coord;
9238 tp->newsegment = FALSE;
9239 tp->has_timestamp = FALSE;
9242 if ( vtl->current_track ) {
9243 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9244 /* Auto attempt to get elevation from DEM data (if it's available) */
9245 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9248 vtl->ct_x1 = vtl->ct_x2;
9249 vtl->ct_y1 = vtl->ct_y2;
9250 vtl->ct_x2 = event->x;
9251 vtl->ct_y2 = event->y;
9253 vik_layer_emit_update ( VIK_LAYER(vtl) );
9257 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9259 // ----------------------------------------------------- if current is a route - switch to new track
9260 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9262 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9263 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9265 new_track_create_common ( vtl, name );
9271 return tool_new_track_or_route_click ( vtl, event, vvp );
9274 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9276 if ( event->button == 2 ) {
9277 // Pan moving ended - enable potential point drawing again
9278 vtl->draw_sync_do = TRUE;
9279 vtl->draw_sync_done = TRUE;
9283 /*** New route ****/
9285 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9290 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9292 // -------------------------- if current is a track - switch to new route
9293 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9295 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9296 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9297 new_route_create_common ( vtl, name );
9303 return tool_new_track_or_route_click ( vtl, event, vvp );
9306 /*** New waypoint ****/
9308 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9313 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9316 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9318 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9319 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9320 trw_layer_calculate_bounds_waypoints ( vtl );
9321 vik_layer_emit_update ( VIK_LAYER(vtl) );
9327 /*** Edit trackpoint ****/
9329 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9331 tool_ed_t *t = g_new(tool_ed_t, 1);
9337 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9342 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9344 tool_ed_t *t = data;
9345 VikViewport *vvp = t->vvp;
9346 TPSearchParams params;
9347 /* OUTDATED DOCUMENTATION:
9348 find 5 pixel range on each side. then put these UTM, and a pointer
9349 to the winning track name (and maybe the winning track itself), and a
9350 pointer to the winning trackpoint, inside an array or struct. pass
9351 this along, do a foreach on the tracks which will do a foreach on the
9354 params.x = event->x;
9355 params.y = event->y;
9356 params.closest_track_id = NULL;
9357 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9358 params.closest_tp = NULL;
9359 params.closest_tpl = NULL;
9360 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9362 if ( event->button != 1 )
9365 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9368 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9371 if ( vtl->current_tpl )
9373 /* 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.) */
9374 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9375 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9380 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9382 if ( current_tr->visible &&
9383 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9384 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9385 marker_begin_move ( t, event->x, event->y );
9391 if ( vtl->tracks_visible )
9392 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9394 if ( params.closest_tp )
9396 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9397 vtl->current_tpl = params.closest_tpl;
9398 vtl->current_tp_id = params.closest_track_id;
9399 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9400 trw_layer_tpwin_init ( vtl );
9401 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9402 vik_layer_emit_update ( VIK_LAYER(vtl) );
9406 if ( vtl->routes_visible )
9407 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9409 if ( params.closest_tp )
9411 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9412 vtl->current_tpl = params.closest_tpl;
9413 vtl->current_tp_id = params.closest_track_id;
9414 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9415 trw_layer_tpwin_init ( vtl );
9416 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9417 vik_layer_emit_update ( VIK_LAYER(vtl) );
9421 /* these aren't the droids you're looking for */
9425 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9427 tool_ed_t *t = data;
9428 VikViewport *vvp = t->vvp;
9430 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9436 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9439 if ( event->state & GDK_CONTROL_MASK )
9441 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9442 if ( tp && tp != vtl->current_tpl->data )
9443 new_coord = tp->coord;
9445 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9448 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9449 marker_moveto ( t, x, y );
9457 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9459 tool_ed_t *t = data;
9460 VikViewport *vvp = t->vvp;
9462 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9464 if ( event->button != 1)
9469 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9472 if ( event->state & GDK_CONTROL_MASK )
9474 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9475 if ( tp && tp != vtl->current_tpl->data )
9476 new_coord = tp->coord;
9479 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9480 if ( vtl->current_tp_track )
9481 vik_track_calculate_bounds ( vtl->current_tp_track );
9483 marker_end_move ( t );
9485 /* diff dist is diff from orig */
9487 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9489 vik_layer_emit_update ( VIK_LAYER(vtl) );
9496 /*** Route Finder ***/
9497 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9502 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9505 if ( !vtl ) return FALSE;
9506 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9507 if ( event->button == 3 && vtl->route_finder_current_track ) {
9509 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9511 vtl->route_finder_coord = *new_end;
9513 vik_layer_emit_update ( VIK_LAYER(vtl) );
9514 /* remove last ' to:...' */
9515 if ( vtl->route_finder_current_track->comment ) {
9516 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9517 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9518 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9519 last_to - vtl->route_finder_current_track->comment - 1);
9520 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9525 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9526 struct LatLon start, end;
9528 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9529 vik_coord_to_latlon ( &(tmp), &end );
9530 vtl->route_finder_coord = tmp; /* for continuations */
9532 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9533 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9534 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9536 vtl->route_finder_check_added_track = TRUE;
9537 vtl->route_finder_started = FALSE;
9540 vik_routing_default_find ( vtl, start, end);
9542 /* see if anything was done -- a track was added or appended to */
9543 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9544 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 ) );
9545 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9546 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9547 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9548 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9551 if ( vtl->route_finder_added_track )
9552 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9554 vtl->route_finder_added_track = NULL;
9555 vtl->route_finder_check_added_track = FALSE;
9556 vtl->route_finder_append = FALSE;
9558 vik_layer_emit_update ( VIK_LAYER(vtl) );
9560 vtl->route_finder_started = TRUE;
9561 vtl->route_finder_coord = tmp;
9562 vtl->route_finder_current_track = NULL;
9567 /*** Show picture ****/
9569 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9574 /* Params are: vvp, event, last match found or NULL */
9575 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9577 if ( wp->image && wp->visible )
9579 gint x, y, slackx, slacky;
9580 GdkEventButton *event = (GdkEventButton *) params[1];
9582 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9583 slackx = wp->image_width / 2;
9584 slacky = wp->image_height / 2;
9585 if ( x <= event->x + slackx && x >= event->x - slackx
9586 && y <= event->y + slacky && y >= event->y - slacky )
9588 params[2] = wp->image; /* we've found a match. however continue searching
9589 * since we want to find the last match -- that
9590 * is, the match that was drawn last. */
9595 static void trw_layer_show_picture ( gpointer pass_along[6] )
9597 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9599 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9602 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9603 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9604 g_free ( quoted_file );
9605 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9607 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() );
9608 g_error_free ( err );
9611 #endif /* WINDOWS */
9614 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9616 gpointer params[3] = { vvp, event, NULL };
9617 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9619 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9622 static gpointer pass_along[6];
9623 pass_along[0] = vtl;
9624 pass_along[5] = params[2];
9625 trw_layer_show_picture ( pass_along );
9626 return TRUE; /* found a match */
9629 return FALSE; /* go through other layers, searching for a match */
9632 /***************************************************************************
9634 ***************************************************************************/
9637 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9639 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9640 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9643 /* Structure for thumbnail creating data used in the background thread */
9645 VikTrwLayer *vtl; // Layer needed for redrawing
9646 GSList *pics; // Image list
9647 } thumbnail_create_thread_data;
9649 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9651 guint total = g_slist_length(tctd->pics), done = 0;
9652 while ( tctd->pics )
9654 a_thumbnails_create ( (gchar *) tctd->pics->data );
9655 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9657 return -1; /* Abort thread */
9659 tctd->pics = tctd->pics->next;
9662 // Redraw to show the thumbnails as they are now created
9663 if ( IS_VIK_LAYER(tctd->vtl) )
9664 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9669 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9671 while ( tctd->pics )
9673 g_free ( tctd->pics->data );
9674 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9679 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9681 if ( ! vtl->has_verified_thumbnails )
9683 GSList *pics = NULL;
9684 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9687 gint len = g_slist_length ( pics );
9688 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9689 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9692 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9694 (vik_thr_func) create_thumbnails_thread,
9696 (vik_thr_free_func) thumbnail_create_thread_free,
9704 static const gchar* my_track_colors ( gint ii )
9706 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9718 // Fast and reliable way of returning a colour
9719 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9722 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9724 GHashTableIter iter;
9725 gpointer key, value;
9729 g_hash_table_iter_init ( &iter, vtl->tracks );
9731 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9733 // Tracks get a random spread of colours if not already assigned
9734 if ( ! VIK_TRACK(value)->has_color ) {
9735 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9736 VIK_TRACK(value)->color = vtl->track_color;
9738 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9740 VIK_TRACK(value)->has_color = TRUE;
9743 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9746 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9752 g_hash_table_iter_init ( &iter, vtl->routes );
9754 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9756 // Routes get an intermix of reds
9757 if ( ! VIK_TRACK(value)->has_color ) {
9759 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9761 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9762 VIK_TRACK(value)->has_color = TRUE;
9765 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9772 * (Re)Calculate the bounds of the waypoints in this layer,
9773 * This should be called whenever waypoints are changed
9775 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9777 struct LatLon topleft = { 0.0, 0.0 };
9778 struct LatLon bottomright = { 0.0, 0.0 };
9781 GHashTableIter iter;
9782 gpointer key, value;
9784 g_hash_table_iter_init ( &iter, vtl->waypoints );
9786 // Set bounds to first point
9787 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9788 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9789 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9792 // Ensure there is another point...
9793 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9795 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9797 // See if this point increases the bounds.
9798 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9800 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9801 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9802 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9803 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9807 vtl->waypoints_bbox.north = topleft.lat;
9808 vtl->waypoints_bbox.east = bottomright.lon;
9809 vtl->waypoints_bbox.south = bottomright.lat;
9810 vtl->waypoints_bbox.west = topleft.lon;
9813 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9815 vik_track_calculate_bounds ( trk );
9818 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9820 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9821 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9824 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9826 if ( ! VIK_LAYER(vtl)->vt )
9829 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9830 if ( g_hash_table_size (vtl->tracks) > 1 )
9831 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9833 if ( g_hash_table_size (vtl->routes) > 1 )
9834 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9836 if ( g_hash_table_size (vtl->waypoints) > 1 )
9837 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9840 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9842 if ( VIK_LAYER(vtl)->realized )
9843 trw_layer_verify_thumbnails ( vtl, vvp );
9844 trw_layer_track_alloc_colors ( vtl );
9846 trw_layer_calculate_bounds_waypoints ( vtl );
9847 trw_layer_calculate_bounds_tracks ( vtl );
9849 // Apply treeview sort after loading all the tracks for this layer
9850 // (rather than sorted insert on each individual track additional)
9851 // and after subsequent changes to the properties as the specified order may have changed.
9852 // since the sorting of a treeview section is now very quick
9853 // NB sorting is also performed after every name change as well to maintain the list order
9854 trw_layer_sort_all ( vtl );
9857 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9859 return vtl->coord_mode;
9863 * Uniquify the whole layer
9864 * Also requires the layers panel as the names shown there need updating too
9865 * Returns whether the operation was successful or not
9867 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9870 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9871 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9872 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9878 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9880 vik_coord_convert ( &(wp->coord), *dest_mode );
9883 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9885 vik_track_convert ( tr, *dest_mode );
9888 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9890 if ( vtl->coord_mode != dest_mode )
9892 vtl->coord_mode = dest_mode;
9893 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9894 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9895 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9899 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9901 vtl->menu_selection = selection;
9904 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9906 return (vtl->menu_selection);
9909 /* ----------- Downloading maps along tracks --------------- */
9911 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9913 /* TODO: calculating based on current size of viewport */
9914 const gdouble w_at_zoom_0_125 = 0.0013;
9915 const gdouble h_at_zoom_0_125 = 0.0011;
9916 gdouble zoom_factor = zoom_level/0.125;
9918 wh->lat = h_at_zoom_0_125 * zoom_factor;
9919 wh->lon = w_at_zoom_0_125 * zoom_factor;
9921 return 0; /* all OK */
9924 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9926 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9927 (dist->lat >= ABS(to->north_south - from->north_south)))
9930 VikCoord *coord = g_malloc(sizeof(VikCoord));
9931 coord->mode = VIK_COORD_LATLON;
9933 if (ABS(gradient) < 1) {
9934 if (from->east_west > to->east_west)
9935 coord->east_west = from->east_west - dist->lon;
9937 coord->east_west = from->east_west + dist->lon;
9938 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9940 if (from->north_south > to->north_south)
9941 coord->north_south = from->north_south - dist->lat;
9943 coord->north_south = from->north_south + dist->lat;
9944 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9950 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9952 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9953 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9955 VikCoord *next = from;
9957 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9959 list = g_list_prepend(list, next);
9965 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9967 typedef struct _Rect {
9972 #define GLRECT(iter) ((Rect *)((iter)->data))
9975 GList *rects_to_download = NULL;
9978 if (get_download_area_width(vvp, zoom_level, &wh))
9981 GList *iter = tr->trackpoints;
9985 gboolean new_map = TRUE;
9986 VikCoord *cur_coord, tl, br;
9989 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9991 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9992 rect = g_malloc(sizeof(Rect));
9995 rect->center = *cur_coord;
9996 rects_to_download = g_list_prepend(rects_to_download, rect);
10001 gboolean found = FALSE;
10002 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10003 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10014 GList *fillins = NULL;
10015 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10016 /* seems that ATM the function get_next_coord works only for LATLON */
10017 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10018 /* fill-ins for far apart points */
10019 GList *cur_rect, *next_rect;
10020 for (cur_rect = rects_to_download;
10021 (next_rect = cur_rect->next) != NULL;
10022 cur_rect = cur_rect->next) {
10023 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10024 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10025 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10029 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10032 GList *iter = fillins;
10034 cur_coord = (VikCoord *)(iter->data);
10035 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10036 rect = g_malloc(sizeof(Rect));
10039 rect->center = *cur_coord;
10040 rects_to_download = g_list_prepend(rects_to_download, rect);
10045 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10046 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10050 for (iter = fillins; iter; iter = iter->next)
10051 g_free(iter->data);
10052 g_list_free(fillins);
10054 if (rects_to_download) {
10055 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10056 g_free(rect_iter->data);
10057 g_list_free(rects_to_download);
10061 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10065 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10066 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10067 gint selected_zoom, default_zoom;
10069 VikTrwLayer *vtl = pass_along[0];
10070 VikLayersPanel *vlp = pass_along[1];
10072 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10073 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10075 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10079 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10081 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10082 int num_maps = g_list_length(vmls);
10085 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10089 // Convert from list of vmls to list of names. Allowing the user to select one of them
10090 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10091 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10093 gchar **np = map_names;
10094 VikMapsLayer **lp = map_layers;
10096 for (i = 0; i < num_maps; i++) {
10097 vml = (VikMapsLayer *)(vmls->data);
10099 *np++ = vik_maps_layer_get_map_label(vml);
10102 // Mark end of the array lists
10106 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10107 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10108 if (cur_zoom == zoom_vals[default_zoom])
10111 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10113 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10116 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10119 for (i = 0; i < num_maps; i++)
10120 g_free(map_names[i]);
10122 g_free(map_layers);
10128 /**** lowest waypoint number calculation ***/
10129 static gint highest_wp_number_name_to_number(const gchar *name) {
10130 if ( strlen(name) == 3 ) {
10131 int n = atoi(name);
10132 if ( n < 100 && name[0] != '0' )
10134 if ( n < 10 && name[0] != '0' )
10142 static void highest_wp_number_reset(VikTrwLayer *vtl)
10144 vtl->highest_wp_number = -1;
10147 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10149 /* if is bigger that top, add it */
10150 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10151 if ( new_wp_num > vtl->highest_wp_number )
10152 vtl->highest_wp_number = new_wp_num;
10155 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10157 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10158 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10159 if ( vtl->highest_wp_number == old_wp_num ) {
10161 vtl->highest_wp_number--;
10163 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10164 /* search down until we find something that *does* exist */
10166 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10167 vtl->highest_wp_number--;
10168 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10173 /* get lowest unused number */
10174 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10177 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10179 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10180 return g_strdup(buf);