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_points_same_position ( gpointer pass_along[6] );
282 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
283 static void trw_layer_reverse ( gpointer pass_along[6] );
284 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
285 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
286 static void trw_layer_show_picture ( gpointer pass_along[6] );
287 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
289 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
290 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
291 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
292 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
293 static void trw_layer_new_wp ( gpointer lav[2] );
294 static void trw_layer_new_track ( gpointer lav[2] );
295 static void trw_layer_new_route ( gpointer lav[2] );
296 static void trw_layer_finish_track ( gpointer lav[2] );
297 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
298 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
299 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
300 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
301 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
302 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
303 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
304 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
305 #ifdef VIK_CONFIG_GEOTAG
306 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
307 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
308 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
309 static void trw_layer_geotagging ( gpointer lav[2] );
311 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
312 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
313 #ifdef VIK_CONFIG_OPENSTREETMAP
314 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
315 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
317 #ifdef VIK_CONFIG_GEOCACHES
318 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
320 #ifdef VIK_CONFIG_GEOTAG
321 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
323 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
324 static void trw_layer_gps_upload ( gpointer lav[2] );
326 // Specific route versions:
327 // Most track handling functions can handle operating on the route list
328 // However these ones are easier in separate functions
329 static void trw_layer_auto_routes_view ( gpointer lav[2] );
330 static void trw_layer_delete_all_routes ( gpointer lav[2] );
331 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
334 static void trw_layer_properties_item ( gpointer pass_along[7] );
335 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
336 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
337 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
339 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
340 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
341 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
343 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
344 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
345 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
346 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
348 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
349 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
350 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
351 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
352 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
353 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
354 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
355 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
356 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
357 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
358 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
359 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
360 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
361 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
362 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
363 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
364 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
365 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
366 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
367 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
368 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
369 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
370 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
372 static void cached_pixbuf_free ( CachedPixbuf *cp );
373 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
375 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
376 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
378 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
379 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
381 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
382 static void highest_wp_number_reset(VikTrwLayer *vtl);
383 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
384 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
386 // Note for the following tool GtkRadioActionEntry texts:
387 // the very first text value is an internal name not displayed anywhere
388 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
389 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
390 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
391 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
392 static VikToolInterface trw_layer_tools[] = {
393 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
394 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
395 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
397 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
399 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
400 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
401 (VikToolMouseFunc) tool_new_track_click,
402 (VikToolMouseMoveFunc) tool_new_track_move,
403 (VikToolMouseFunc) tool_new_track_release,
404 (VikToolKeyFunc) tool_new_track_key_press,
405 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
406 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
408 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
409 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
410 (VikToolMouseFunc) tool_new_route_click,
411 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
412 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
413 (VikToolKeyFunc) tool_new_track_key_press, // -/#
414 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
415 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
417 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
418 (VikToolConstructorFunc) tool_edit_waypoint_create,
419 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
421 (VikToolMouseFunc) tool_edit_waypoint_click,
422 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
423 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
425 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
427 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
428 (VikToolConstructorFunc) tool_edit_trackpoint_create,
429 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
431 (VikToolMouseFunc) tool_edit_trackpoint_click,
432 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
433 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
435 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
437 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
438 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
439 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
441 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
443 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
444 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
445 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
447 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
451 TOOL_CREATE_WAYPOINT=0,
455 TOOL_EDIT_TRACKPOINT,
461 /****** PARAMETERS ******/
463 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
464 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
466 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
467 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
469 #define MIN_POINT_SIZE 2
470 #define MAX_POINT_SIZE 10
472 #define MIN_ARROW_SIZE 3
473 #define MAX_ARROW_SIZE 20
475 static VikLayerParamScale params_scales[] = {
476 /* min max step digits */
477 { 1, 10, 1, 0 }, /* line_thickness */
478 { 0, 100, 1, 0 }, /* track draw speed factor */
479 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
480 /* 5 * step == how much to turn */
481 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
482 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
483 { 5, 500, 5, 0 }, // 5: image cache_size - " "
484 { 0, 8, 1, 0 }, // 6: Background line thickness
485 { 1, 64, 1, 0 }, /* wpsize */
486 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
487 { 1, 100, 1, 0 }, // 9: elevation factor
488 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
489 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
492 static gchar* params_font_sizes[] = {
493 N_("Extra Extra Small"),
499 N_("Extra Extra Large"),
502 // Needs to align with vik_layer_sort_order_t
503 static gchar* params_sort_order[] = {
505 N_("Name Ascending"),
506 N_("Name Descending"),
510 static VikLayerParamData black_color_default ( void ) {
511 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
513 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
514 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
515 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
516 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
517 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
518 static VikLayerParamData trackbgcolor_default ( void ) {
519 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
521 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
522 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
523 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
525 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
526 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
527 static VikLayerParamData wptextcolor_default ( void ) {
528 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
530 static VikLayerParamData wpbgcolor_default ( void ) {
531 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
533 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
534 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
536 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
537 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
538 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
540 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
542 VikLayerParam trw_layer_params[] = {
543 { 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 },
544 { 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 },
545 { 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 },
547 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
548 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
549 { 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 },
550 { 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 },
551 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
552 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
553 { 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 },
554 { 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 },
555 { 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 },
556 { 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 },
557 { 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 },
558 { 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 },
559 { 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 },
560 { 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 },
561 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
562 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 },
563 { 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 },
565 { 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 },
566 { 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 },
567 { 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,
568 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
569 { 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 },
571 { 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 },
572 { 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 },
573 { 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 },
574 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
575 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
576 { 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 },
577 { 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 },
578 { 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 },
579 { 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 },
580 { 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 },
582 { 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 },
583 { 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 },
584 { 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 },
585 { 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 },
588 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
590 // Sublayer visibilities
633 *** 1) Add to trw_layer_params and enumeration
634 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
637 /****** END PARAMETERS ******/
639 /* Layer Interface function definitions */
640 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
641 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
642 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
643 static void trw_layer_free ( VikTrwLayer *trwlayer );
644 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
645 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
646 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
647 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
648 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
649 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
650 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
651 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
652 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
653 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
654 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
655 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
656 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
657 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
658 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
659 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
660 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
661 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
662 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
663 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
664 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
665 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
666 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
667 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
668 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
669 /* End Layer Interface function definitions */
671 VikLayerInterface vik_trw_layer_interface = {
678 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
682 params_groups, /* params_groups */
683 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
687 (VikLayerFuncCreate) trw_layer_create,
688 (VikLayerFuncRealize) trw_layer_realize,
689 (VikLayerFuncPostRead) trw_layer_post_read,
690 (VikLayerFuncFree) trw_layer_free,
692 (VikLayerFuncProperties) NULL,
693 (VikLayerFuncDraw) trw_layer_draw,
694 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
696 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
697 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
699 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
700 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
702 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
703 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
704 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
705 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
706 (VikLayerFuncLayerSelected) trw_layer_selected,
708 (VikLayerFuncMarshall) trw_layer_marshall,
709 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
711 (VikLayerFuncSetParam) trw_layer_set_param,
712 (VikLayerFuncGetParam) trw_layer_get_param,
714 (VikLayerFuncReadFileData) a_gpspoint_read_file,
715 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
717 (VikLayerFuncDeleteItem) trw_layer_del_item,
718 (VikLayerFuncCutItem) trw_layer_cut_item,
719 (VikLayerFuncCopyItem) trw_layer_copy_item,
720 (VikLayerFuncPasteItem) trw_layer_paste_item,
721 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
723 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
725 (VikLayerFuncSelectClick) trw_layer_select_click,
726 (VikLayerFuncSelectMove) trw_layer_select_move,
727 (VikLayerFuncSelectRelease) trw_layer_select_release,
728 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
731 GType vik_trw_layer_get_type ()
733 static GType vtl_type = 0;
737 static const GTypeInfo vtl_info =
739 sizeof (VikTrwLayerClass),
740 NULL, /* base_init */
741 NULL, /* base_finalize */
742 NULL, /* class init */
743 NULL, /* class_finalize */
744 NULL, /* class_data */
745 sizeof (VikTrwLayer),
747 NULL /* instance init */
749 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
755 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
757 static gpointer pass_along[6];
763 pass_along[1] = NULL;
764 pass_along[2] = GINT_TO_POINTER (subtype);
765 pass_along[3] = sublayer;
766 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
767 pass_along[5] = NULL;
769 trw_layer_delete_item ( pass_along );
772 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
774 static gpointer pass_along[6];
780 pass_along[1] = NULL;
781 pass_along[2] = GINT_TO_POINTER (subtype);
782 pass_along[3] = sublayer;
783 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
784 pass_along[5] = NULL;
786 trw_layer_copy_item_cb(pass_along);
787 trw_layer_cut_item_cb(pass_along);
790 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
792 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
793 gint subtype = GPOINTER_TO_INT (pass_along[2]);
794 gpointer * sublayer = pass_along[3];
798 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
802 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
803 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
804 if ( wp && wp->name )
807 name = NULL; // Broken :(
809 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
810 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
811 if ( trk && trk->name )
814 name = NULL; // Broken :(
817 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
818 if ( trk && trk->name )
821 name = NULL; // Broken :(
824 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
825 subtype, len, name, data);
829 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
831 trw_layer_copy_item_cb(pass_along);
832 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
833 trw_layer_delete_item(pass_along);
836 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
838 // Slightly cheating method, routing via the panels capability
839 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
842 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
852 GByteArray *ba = g_byte_array_new ();
854 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
855 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
856 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
857 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
859 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
862 g_byte_array_append ( ba, id, il );
870 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
877 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
881 w = vik_waypoint_unmarshall ( item, len );
882 // When copying - we'll create a new name based on the original
883 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
884 vik_trw_layer_add_waypoint ( vtl, name, w );
885 waypoint_convert (NULL, w, &vtl->coord_mode);
888 trw_layer_calculate_bounds_waypoints ( vtl );
890 // Consider if redraw necessary for the new item
891 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
892 vik_layer_emit_update ( VIK_LAYER(vtl) );
895 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
899 t = vik_track_unmarshall ( item, len );
900 // When copying - we'll create a new name based on the original
901 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
902 vik_trw_layer_add_track ( vtl, name, t );
903 vik_track_convert (t, vtl->coord_mode);
906 // Consider if redraw necessary for the new item
907 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
908 vik_layer_emit_update ( VIK_LAYER(vtl) );
911 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
915 t = vik_track_unmarshall ( item, len );
916 // When copying - we'll create a new name based on the original
917 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
918 vik_trw_layer_add_route ( vtl, name, t );
919 vik_track_convert (t, vtl->coord_mode);
922 // Consider if redraw necessary for the new item
923 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
924 vik_layer_emit_update ( VIK_LAYER(vtl) );
930 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
937 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
941 case PARAM_TV: vtl->tracks_visible = data.b; break;
942 case PARAM_WV: vtl->waypoints_visible = data.b; break;
943 case PARAM_RV: vtl->routes_visible = data.b; break;
944 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
945 case PARAM_TLFONTSIZE:
946 if ( data.u < FS_NUM_SIZES ) {
947 vtl->track_font_size = data.u;
948 g_free ( vtl->track_fsize_str );
949 switch ( vtl->track_font_size ) {
950 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
951 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
952 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
953 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
954 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
955 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
956 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
960 case PARAM_DM: vtl->drawmode = data.u; break;
962 vtl->track_color = data.c;
963 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
965 case PARAM_DP: vtl->drawpoints = data.b; break;
967 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
968 vtl->drawpoints_size = data.u;
970 case PARAM_DE: vtl->drawelevation = data.b; break;
971 case PARAM_DS: vtl->drawstops = data.b; break;
972 case PARAM_DL: vtl->drawlines = data.b; break;
973 case PARAM_DD: vtl->drawdirections = data.b; break;
975 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
976 vtl->drawdirections_size = data.u;
978 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
979 vtl->stop_length = data.u;
981 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
982 vtl->elevation_factor = data.u;
984 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
986 vtl->line_thickness = data.u;
987 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
990 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
992 vtl->bg_line_thickness = data.u;
993 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
997 vtl->track_bg_color = data.c;
998 if ( vtl->track_bg_gc )
999 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1001 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1002 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1003 case PARAM_DLA: vtl->drawlabels = data.b; break;
1004 case PARAM_DI: vtl->drawimages = data.b; break;
1005 case PARAM_IS: if ( data.u != vtl->image_size )
1007 vtl->image_size = data.u;
1008 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1009 g_queue_free ( vtl->image_cache );
1010 vtl->image_cache = g_queue_new ();
1013 case PARAM_IA: vtl->image_alpha = data.u; break;
1014 case PARAM_ICS: vtl->image_cache_size = data.u;
1015 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1016 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1019 vtl->waypoint_color = data.c;
1020 if ( vtl->waypoint_gc )
1021 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1024 vtl->waypoint_text_color = data.c;
1025 if ( vtl->waypoint_text_gc )
1026 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1029 vtl->waypoint_bg_color = data.c;
1030 if ( vtl->waypoint_bg_gc )
1031 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1034 vtl->wpbgand = data.b;
1035 if ( vtl->waypoint_bg_gc )
1036 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1038 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1039 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1040 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1041 case PARAM_WPFONTSIZE:
1042 if ( data.u < FS_NUM_SIZES ) {
1043 vtl->wp_font_size = data.u;
1044 g_free ( vtl->wp_fsize_str );
1045 switch ( vtl->wp_font_size ) {
1046 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1047 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1048 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1049 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1050 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1051 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1052 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1056 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1061 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1063 VikLayerParamData rv;
1066 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1067 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1068 case PARAM_RV: rv.b = vtl->routes_visible; break;
1069 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1070 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1071 case PARAM_DM: rv.u = vtl->drawmode; break;
1072 case PARAM_TC: rv.c = vtl->track_color; break;
1073 case PARAM_DP: rv.b = vtl->drawpoints; break;
1074 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1075 case PARAM_DE: rv.b = vtl->drawelevation; break;
1076 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1077 case PARAM_DS: rv.b = vtl->drawstops; break;
1078 case PARAM_SL: rv.u = vtl->stop_length; break;
1079 case PARAM_DL: rv.b = vtl->drawlines; break;
1080 case PARAM_DD: rv.b = vtl->drawdirections; break;
1081 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1082 case PARAM_LT: rv.u = vtl->line_thickness; break;
1083 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1084 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1085 case PARAM_DI: rv.b = vtl->drawimages; break;
1086 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1087 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1088 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1089 case PARAM_IS: rv.u = vtl->image_size; break;
1090 case PARAM_IA: rv.u = vtl->image_alpha; break;
1091 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1092 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1093 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1094 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1095 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1096 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1097 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1098 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1099 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1100 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1105 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1112 // Use byte arrays to store sublayer data
1113 // much like done elsewhere e.g. vik_layer_marshall_params()
1114 GByteArray *ba = g_byte_array_new ( );
1119 guint object_length;
1122 // the length of the item
1123 // the sublayer type of item
1124 // the the actual item
1125 #define tlm_append(object_pointer, size, type) \
1127 object_length = (size); \
1128 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1129 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1130 g_byte_array_append ( ba, (object_pointer), object_length );
1132 // Layer parameters first
1133 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1134 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1135 g_byte_array_append ( ba, pd, pl );
1138 // Now sublayer data
1139 GHashTableIter iter;
1140 gpointer key, value;
1143 g_hash_table_iter_init ( &iter, vtl->waypoints );
1144 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1145 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1146 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1151 g_hash_table_iter_init ( &iter, vtl->tracks );
1152 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1153 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1154 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1159 g_hash_table_iter_init ( &iter, vtl->routes );
1160 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1161 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1162 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1172 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1174 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1176 gint consumed_length;
1178 // First the overall layer parameters
1179 memcpy(&pl, data, sizeof(pl));
1181 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1184 consumed_length = pl;
1185 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1187 #define tlm_size (*(gint *)data)
1188 // See marshalling above for order of how this is written
1190 data += sizeof_len_and_subtype + tlm_size;
1192 // Now the individual sublayers:
1194 while ( *data && consumed_length < len ) {
1195 // Normally four extra bytes at the end of the datastream
1196 // (since it's a GByteArray and that's where it's length is stored)
1197 // So only attempt read when there's an actual block of sublayer data
1198 if ( consumed_length + tlm_size < len ) {
1200 // Reuse pl to read the subtype from the data stream
1201 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1203 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1204 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1205 gchar *name = g_strdup ( trk->name );
1206 vik_trw_layer_add_track ( vtl, name, trk );
1209 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1210 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1211 gchar *name = g_strdup ( wp->name );
1212 vik_trw_layer_add_waypoint ( vtl, name, wp );
1215 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1216 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1217 gchar *name = g_strdup ( trk->name );
1218 vik_trw_layer_add_route ( vtl, name, trk );
1222 consumed_length += tlm_size + sizeof_len_and_subtype;
1225 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1227 // Not stored anywhere else so need to regenerate
1228 trw_layer_calculate_bounds_waypoints ( vtl );
1233 // Keep interesting hash function at least visible
1235 static guint strcase_hash(gconstpointer v)
1237 // 31 bit hash function
1240 gchar s[128]; // malloc is too slow for reading big files
1243 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1244 p[i] = toupper(t[i]);
1250 for (p += 1; *p != '\0'; p++)
1251 h = (h << 5) - h + *p;
1258 // Stick a 1 at the end of the function name to make it more unique
1259 // thus more easily searchable in a simple text editor
1260 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1262 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1263 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1265 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1266 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1268 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1269 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1270 // and with normal PC processing capabilities - it has negligibile performance impact
1271 // This also minimized the amount of rework - as the management of the hash tables already exists.
1273 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1274 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1275 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1277 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1278 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1279 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1280 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1281 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1282 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1284 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1286 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1288 // Param settings that are not available via the GUI
1289 // Force to on after processing params (which defaults them to off with a zero value)
1290 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1292 rv->draw_sync_done = TRUE;
1293 rv->draw_sync_do = TRUE;
1294 // Everything else is 0, FALSE or NULL
1300 static void trw_layer_free ( VikTrwLayer *trwlayer )
1302 g_hash_table_destroy(trwlayer->waypoints);
1303 g_hash_table_destroy(trwlayer->waypoints_iters);
1304 g_hash_table_destroy(trwlayer->tracks);
1305 g_hash_table_destroy(trwlayer->tracks_iters);
1306 g_hash_table_destroy(trwlayer->routes);
1307 g_hash_table_destroy(trwlayer->routes_iters);
1309 /* ODC: replace with GArray */
1310 trw_layer_free_track_gcs ( trwlayer );
1312 if ( trwlayer->wp_right_click_menu )
1313 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1315 if ( trwlayer->track_right_click_menu )
1316 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1318 if ( trwlayer->tracklabellayout != NULL)
1319 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1321 if ( trwlayer->wplabellayout != NULL)
1322 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1324 if ( trwlayer->waypoint_gc != NULL )
1325 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1327 if ( trwlayer->waypoint_text_gc != NULL )
1328 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1330 if ( trwlayer->waypoint_bg_gc != NULL )
1331 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1333 g_free ( trwlayer->wp_fsize_str );
1334 g_free ( trwlayer->track_fsize_str );
1336 if ( trwlayer->tpwin != NULL )
1337 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1339 if ( trwlayer->tracks_analysis_dialog != NULL )
1340 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1342 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1343 g_queue_free ( trwlayer->image_cache );
1346 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1350 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1351 dp->xmpp = vik_viewport_get_xmpp ( vp );
1352 dp->ympp = vik_viewport_get_ympp ( vp );
1353 dp->width = vik_viewport_get_width ( vp );
1354 dp->height = vik_viewport_get_height ( vp );
1355 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1356 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1358 dp->center = vik_viewport_get_center ( vp );
1359 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1360 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1365 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1366 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1367 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1369 dp->ce1 = dp->center->east_west-w2;
1370 dp->ce2 = dp->center->east_west+w2;
1371 dp->cn1 = dp->center->north_south-h2;
1372 dp->cn2 = dp->center->north_south+h2;
1373 } else if ( dp->lat_lon ) {
1374 VikCoord upperleft, bottomright;
1375 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1376 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1377 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1378 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1379 dp->ce1 = upperleft.east_west;
1380 dp->ce2 = bottomright.east_west;
1381 dp->cn1 = bottomright.north_south;
1382 dp->cn2 = upperleft.north_south;
1385 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1389 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1390 * Here a simple traffic like light colour system is used:
1391 * . slow points are red
1392 * . average is yellow
1393 * . fast points are green
1395 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1398 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1399 if ( average_speed > 0 ) {
1400 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1401 if ( rv < low_speed )
1402 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1403 else if ( rv > high_speed )
1404 return VIK_TRW_LAYER_TRACK_GC_FAST;
1406 return VIK_TRW_LAYER_TRACK_GC_AVER;
1409 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1412 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1414 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1415 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1416 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1417 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1421 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1423 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1425 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1426 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1428 // Fallback if parse failure
1429 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1431 g_free ( label_markup );
1433 gint label_x, label_y;
1435 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1437 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1438 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1442 * distance_in_preferred_units:
1443 * @dist: The source distance in standard SI Units (i.e. metres)
1445 * TODO: This is a generic function that could be moved into globals.c or utils.c
1447 * Probably best used if you have a only few conversions to perform.
1448 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1449 * since it will be doing the preference check on each call
1451 * Returns: The distance in the units as specified by the preferences
1453 static gdouble distance_in_preferred_units ( gdouble dist )
1456 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1457 switch (dist_units) {
1458 case VIK_UNITS_DISTANCE_MILES:
1459 mydist = VIK_METERS_TO_MILES(dist);
1461 // VIK_UNITS_DISTANCE_KILOMETRES:
1463 mydist = dist/1000.0;
1470 * trw_layer_draw_dist_labels:
1472 * Draw a few labels along a track at nicely seperated distances
1473 * This might slow things down if there's many tracks being displayed with this on.
1475 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1477 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1478 25.0, 40.0, 50.0, 75.0, 100.0,
1479 150.0, 200.0, 250.0, 500.0, 1000.0};
1481 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1483 // Convert to specified unit to find the friendly breakdown value
1484 dist = distance_in_preferred_units ( dist );
1488 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1489 if ( chunksd[i] > dist ) {
1491 dist = chunksd[index];
1496 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1498 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1499 gdouble dist_i = dist * i;
1501 // Convert distance back into metres for use in finding a trackpoint
1502 switch (dist_units) {
1503 case VIK_UNITS_DISTANCE_MILES:
1504 dist_i = VIK_MILES_TO_METERS(dist_i);
1506 // VIK_UNITS_DISTANCE_KILOMETRES:
1508 dist_i = dist_i*1000.0;
1512 gdouble dist_current = 0.0;
1513 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1514 gdouble dist_next = 0.0;
1515 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1517 gdouble dist_between_tps = fabs (dist_next - dist_current);
1518 gdouble ratio = 0.0;
1519 // Prevent division by 0 errors
1520 if ( dist_between_tps > 0.0 )
1521 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1523 if ( tp_current && tp_next ) {
1524 // Construct the name based on the distance value
1527 switch (dist_units) {
1528 case VIK_UNITS_DISTANCE_MILES:
1529 units = g_strdup ( _("miles") );
1531 // VIK_UNITS_DISTANCE_KILOMETRES:
1533 units = g_strdup ( _("km") );
1537 // Convert for display
1538 dist_i = distance_in_preferred_units ( dist_i );
1540 // Make the precision of the output related to the unit size.
1542 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1543 else if ( index == 1 )
1544 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1546 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1549 struct LatLon ll_current, ll_next;
1550 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1551 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1553 // positional interpolation
1554 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1555 // but should be good enough over the small scale that I anticipate usage on
1556 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1557 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1559 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1562 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1563 fgcolour = gdk_color_to_string ( &(trk->color) );
1565 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1567 // if highlight mode on, then colour the background in the highlight colour
1569 if ( drawing_highlight )
1570 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1572 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1574 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1576 g_free ( fgcolour );
1577 g_free ( bgcolour );
1584 * trw_layer_draw_track_name_labels:
1586 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1588 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1591 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1592 fgcolour = gdk_color_to_string ( &(trk->color) );
1594 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1596 // if highlight mode on, then colour the background in the highlight colour
1598 if ( drawing_highlight )
1599 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1601 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1603 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1605 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1606 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1607 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1608 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1609 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1610 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1612 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1614 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1617 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1618 // No other labels to draw
1621 GList *ending = g_list_last ( trk->trackpoints );
1622 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1623 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1625 gboolean done_start_end = FALSE;
1627 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1628 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1630 // This number can be configured via the settings if you really want to change it
1631 gdouble distance_diff;
1632 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1633 distance_diff = 100.0; // Metres
1635 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1636 // Start and end 'close' together so only draw one label at an average location
1637 gint x1, x2, y1, y2;
1638 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1639 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1641 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1643 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1644 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1647 done_start_end = TRUE;
1651 if ( ! done_start_end ) {
1652 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1653 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1654 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1655 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1656 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1657 g_free ( name_start );
1659 // Don't draw end label if this is the one being created
1660 if ( trk != dp->vtl->current_track ) {
1661 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1662 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1663 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1664 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1665 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1666 g_free ( name_end );
1671 g_free ( fgcolour );
1672 g_free ( bgcolour );
1676 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1678 if ( ! track->visible )
1681 /* TODO: this function is a mess, get rid of any redundancy */
1682 GList *list = track->trackpoints;
1684 gboolean useoldvals = TRUE;
1686 gboolean drawpoints;
1688 gboolean drawelevation;
1689 gdouble min_alt, max_alt, alt_diff = 0;
1691 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1692 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1695 if ( dp->vtl->drawelevation )
1697 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1698 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1699 alt_diff = max_alt - min_alt;
1702 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1703 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1704 trw_layer_draw_track ( id, track, dp, TRUE );
1706 if ( draw_track_outline )
1707 drawpoints = drawstops = FALSE;
1709 drawpoints = dp->vtl->drawpoints;
1710 drawstops = dp->vtl->drawstops;
1713 gboolean drawing_highlight = FALSE;
1714 /* Current track - used for creation */
1715 if ( track == dp->vtl->current_track )
1716 main_gc = dp->vtl->current_track_gc;
1718 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1719 /* Draw all tracks of the layer in special colour */
1720 /* if track is member of selected layer or is the current selected track
1721 then draw in the highlight colour.
1722 NB this supercedes the drawmode */
1723 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1724 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1725 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1726 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1727 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1728 drawing_highlight = TRUE;
1731 if ( !drawing_highlight ) {
1732 // Still need to figure out the gc according to the drawing mode:
1733 switch ( dp->vtl->drawmode ) {
1734 case DRAWMODE_BY_TRACK:
1735 if ( dp->vtl->track_1color_gc )
1736 g_object_unref ( dp->vtl->track_1color_gc );
1737 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1738 main_gc = dp->vtl->track_1color_gc;
1741 // Mostly for DRAWMODE_ALL_SAME_COLOR
1742 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1743 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1750 int x, y, oldx, oldy;
1751 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1753 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1755 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1757 // Draw the first point as something a bit different from the normal points
1758 // ATM it's slightly bigger and a triangle
1760 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1761 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1767 gdouble average_speed = 0.0;
1768 gdouble low_speed = 0.0;
1769 gdouble high_speed = 0.0;
1770 // If necessary calculate these values - which is done only once per track redraw
1771 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1772 // the percentage factor away from the average speed determines transistions between the levels
1773 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1774 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1775 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1778 while ((list = g_list_next(list)))
1780 tp = VIK_TRACKPOINT(list->data);
1781 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1783 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1784 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1785 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1786 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1787 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1789 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1792 * If points are the same in display coordinates, don't draw.
1794 if ( useoldvals && x == oldx && y == oldy )
1796 // Still need to process points to ensure 'stops' are drawn if required
1797 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1798 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1799 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 );
1804 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1805 if ( drawpoints || dp->vtl->drawlines ) {
1806 // setup main_gc for both point and line drawing
1807 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1808 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 ) );
1812 if ( drawpoints && ! draw_track_outline )
1817 * The concept of drawing stops is that a trackpoint
1818 * that is if the next trackpoint has a timestamp far into
1819 * the future, we draw a circle of 6x trackpoint size,
1820 * instead of a rectangle of 2x trackpoint size.
1821 * This is drawn first so the trackpoint will be drawn on top
1824 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1825 /* Stop point. Draw 6x circle. Always in redish colour */
1826 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 );
1828 /* Regular point - draw 2x square. */
1829 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1832 /* Final point - draw 4x circle. */
1833 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 );
1836 if ((!tp->newsegment) && (dp->vtl->drawlines))
1839 /* UTM only: zone check */
1840 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1841 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1844 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1846 if ( draw_track_outline ) {
1847 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1851 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1853 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1855 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1860 tmp[1].y = oldy-FIXALTITUDE(list->data);
1862 tmp[2].y = y-FIXALTITUDE(list->next->data);
1867 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1868 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1870 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1871 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1873 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1878 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1879 // Draw an arrow at the mid point to show the direction of the track
1880 // Code is a rework from vikwindow::draw_ruler()
1881 gint midx = (oldx + x) / 2;
1882 gint midy = (oldy + y) / 2;
1884 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1885 // Avoid divide by zero and ensure at least 1 pixel big
1887 gdouble dx = (oldx - midx) / len;
1888 gdouble dy = (oldy - midy) / len;
1889 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
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) );
1900 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1902 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1903 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1905 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1907 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1908 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 ));
1912 * If points are the same in display coordinates, don't draw.
1914 if ( x != oldx || y != oldy )
1916 if ( draw_track_outline )
1917 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1919 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1925 * If points are the same in display coordinates, don't draw.
1927 if ( x != oldx && y != oldy )
1929 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1930 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1938 // Labels drawn after the trackpoints, so the labels are on top
1939 if ( dp->vtl->track_draw_labels ) {
1940 if ( track->max_number_dist_labels > 0 ) {
1941 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
1944 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
1945 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
1951 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1953 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1954 trw_layer_draw_track ( id, track, dp, FALSE );
1958 static void cached_pixbuf_free ( CachedPixbuf *cp )
1960 g_object_unref ( G_OBJECT(cp->pixbuf) );
1961 g_free ( cp->image );
1964 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1966 return strcmp ( cp->image, name );
1969 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1972 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1973 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1974 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1977 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1979 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1981 if ( wp->image && dp->vtl->drawimages )
1983 GdkPixbuf *pixbuf = NULL;
1986 if ( dp->vtl->image_alpha == 0)
1989 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1991 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1994 gchar *image = wp->image;
1995 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1996 if ( ! regularthumb )
1998 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1999 image = "\x12\x00"; /* this shouldn't occur naturally. */
2003 CachedPixbuf *cp = NULL;
2004 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2005 if ( dp->vtl->image_size == 128 )
2006 cp->pixbuf = regularthumb;
2009 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2010 g_assert ( cp->pixbuf );
2011 g_object_unref ( G_OBJECT(regularthumb) );
2013 cp->image = g_strdup ( image );
2015 /* needed so 'click picture' tool knows how big the pic is; we don't
2016 * store it in cp because they may have been freed already. */
2017 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2018 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2020 g_queue_push_head ( dp->vtl->image_cache, cp );
2021 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2022 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2024 pixbuf = cp->pixbuf;
2028 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2034 w = gdk_pixbuf_get_width ( pixbuf );
2035 h = gdk_pixbuf_get_height ( pixbuf );
2037 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2039 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2040 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2041 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2042 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2043 // Highlighted - so draw a little border around the chosen one
2044 // single line seems a little weak so draw 2 of them
2045 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2046 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2047 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2048 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2051 if ( dp->vtl->image_alpha == 255 )
2052 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2054 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2056 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2060 // Draw appropriate symbol - either symbol image or simple types
2061 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2062 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 );
2064 else if ( wp == dp->vtl->current_wp ) {
2065 switch ( dp->vtl->wp_symbol ) {
2066 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;
2067 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;
2068 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;
2069 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 );
2070 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 );
2074 switch ( dp->vtl->wp_symbol ) {
2075 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;
2076 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;
2077 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;
2078 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 );
2079 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;
2083 if ( dp->vtl->drawlabels )
2085 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2086 gint label_x, label_y;
2088 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2090 // Could this stored in the waypoint rather than recreating each pass?
2091 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2093 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2094 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2096 // Fallback if parse failure
2097 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2099 g_free ( wp_label_markup );
2101 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2102 label_x = x - width/2;
2103 if ( wp->symbol_pixbuf )
2104 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2106 label_y = y - dp->vtl->wp_size - height - 2;
2108 /* if highlight mode on, then draw background text in highlight colour */
2109 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2110 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2111 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2112 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2113 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2115 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2118 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2120 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2125 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2127 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2128 trw_layer_draw_waypoint ( id, wp, dp );
2132 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2134 static struct DrawingParams dp;
2135 g_assert ( l != NULL );
2137 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2139 if ( l->tracks_visible )
2140 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2142 if ( l->routes_visible )
2143 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2145 if (l->waypoints_visible)
2146 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2149 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2152 if ( vtl->track_bg_gc )
2154 g_object_unref ( vtl->track_bg_gc );
2155 vtl->track_bg_gc = NULL;
2157 if ( vtl->track_1color_gc )
2159 g_object_unref ( vtl->track_1color_gc );
2160 vtl->track_1color_gc = NULL;
2162 if ( vtl->current_track_gc )
2164 g_object_unref ( vtl->current_track_gc );
2165 vtl->current_track_gc = NULL;
2167 if ( vtl->current_track_newpoint_gc )
2169 g_object_unref ( vtl->current_track_newpoint_gc );
2170 vtl->current_track_newpoint_gc = NULL;
2173 if ( ! vtl->track_gc )
2175 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2176 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2177 g_array_free ( vtl->track_gc, TRUE );
2178 vtl->track_gc = NULL;
2181 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2183 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2184 gint width = vtl->line_thickness;
2186 if ( vtl->track_gc )
2187 trw_layer_free_track_gcs ( vtl );
2189 if ( vtl->track_bg_gc )
2190 g_object_unref ( vtl->track_bg_gc );
2191 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2193 // Ensure new track drawing heeds line thickness setting
2194 // however always have a minium of 2, as 1 pixel is really narrow
2195 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2197 if ( vtl->current_track_gc )
2198 g_object_unref ( vtl->current_track_gc );
2199 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2200 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2202 // 'newpoint' gc is exactly the same as the current track gc
2203 if ( vtl->current_track_newpoint_gc )
2204 g_object_unref ( vtl->current_track_newpoint_gc );
2205 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2206 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2208 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2210 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2211 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2213 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2214 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2215 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2217 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2219 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2222 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2224 VikTrwLayer *rv = trw_layer_new1 ( vp );
2225 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2227 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2228 /* early exit, as the rest is GUI related */
2232 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2233 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2235 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2236 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2238 trw_layer_new_track_gcs ( rv, vp );
2240 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2241 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2242 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2243 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2245 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2247 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2252 #define SMALL_ICON_SIZE 18
2254 * Can accept a null symbol, and may return null value
2256 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2258 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2259 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2260 // So needing a small icon for the treeview may need some resizing:
2261 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2262 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2266 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2268 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2270 GdkPixbuf *pixbuf = NULL;
2272 if ( track->has_color ) {
2273 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2274 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2275 // Here is some magic found to do the conversion
2276 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2277 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2278 ((track->color.green & 0xff00) << 8) |
2279 (track->color.blue & 0xff00);
2281 gdk_pixbuf_fill ( pixbuf, pixel );
2284 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 );
2287 g_object_unref (pixbuf);
2289 *new_iter = *((GtkTreeIter *) pass_along[1]);
2290 if ( track->is_route )
2291 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2293 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2295 if ( ! track->visible )
2296 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2299 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2301 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2303 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 );
2305 *new_iter = *((GtkTreeIter *) pass_along[1]);
2306 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2308 if ( ! wp->visible )
2309 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2312 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2314 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2317 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2319 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2322 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2324 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2327 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2330 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2332 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2333 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2335 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2337 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2340 if ( g_hash_table_size (vtl->routes) > 0 ) {
2341 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2343 pass_along[0] = &(vtl->routes_iter);
2344 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2346 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2348 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2351 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2352 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2354 pass_along[0] = &(vtl->waypoints_iter);
2355 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2357 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2359 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2364 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2368 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2369 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2370 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2371 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2373 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2375 return (t->visible ^= 1);
2379 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2381 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2383 return (t->visible ^= 1);
2387 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2389 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2391 return (t->visible ^= 1);
2400 * Return a property about tracks for this layer
2402 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2404 return vtl->line_thickness;
2407 // Structure to hold multiple track information for a layer
2416 * Build up layer multiple track information via updating the tooltip_tracks structure
2418 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2420 tt->length = tt->length + vik_track_get_length (tr);
2422 // Ensure times are available
2423 if ( tr->trackpoints &&
2424 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2425 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2428 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2429 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2431 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2432 // Hence initialize to the first 'proper' value
2433 if ( tt->start_time == 0 )
2434 tt->start_time = t1;
2435 if ( tt->end_time == 0 )
2438 // Update find the earliest / last times
2439 if ( t1 < tt->start_time )
2440 tt->start_time = t1;
2441 if ( t2 > tt->end_time )
2444 // Keep track of total time
2445 // there maybe gaps within a track (eg segments)
2446 // but this should be generally good enough for a simple indicator
2447 tt->duration = tt->duration + (int)(t2-t1);
2452 * Generate tooltip text for the layer.
2453 * This is relatively complicated as it considers information for
2454 * no tracks, a single track or multiple tracks
2455 * (which may or may not have timing information)
2457 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2468 static gchar tmp_buf[128];
2471 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2473 // Safety check - I think these should always be valid
2474 if ( vtl->tracks && vtl->waypoints ) {
2475 tooltip_tracks tt = { 0.0, 0, 0 };
2476 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2478 GDate* gdate_start = g_date_new ();
2479 g_date_set_time_t (gdate_start, tt.start_time);
2481 GDate* gdate_end = g_date_new ();
2482 g_date_set_time_t (gdate_end, tt.end_time);
2484 if ( g_date_compare (gdate_start, gdate_end) ) {
2485 // Dates differ so print range on separate line
2486 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2487 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2488 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2491 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2492 if ( tt.start_time != 0 )
2493 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2497 if ( tt.length > 0.0 ) {
2498 gdouble len_in_units;
2500 // Setup info dependent on distance units
2501 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2502 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2503 len_in_units = VIK_METERS_TO_MILES(tt.length);
2506 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2507 len_in_units = tt.length/1000.0;
2510 // Timing information if available
2512 if ( tt.duration > 0 ) {
2513 g_snprintf (tbuf1, sizeof(tbuf1),
2514 _(" in %d:%02d hrs:mins"),
2515 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2517 g_snprintf (tbuf2, sizeof(tbuf2),
2518 _("\n%sTotal Length %.1f %s%s"),
2519 tbuf3, len_in_units, tbuf4, tbuf1);
2522 // Put together all the elements to form compact tooltip text
2523 g_snprintf (tmp_buf, sizeof(tmp_buf),
2524 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2525 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2527 g_date_free (gdate_start);
2528 g_date_free (gdate_end);
2535 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2539 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2541 // Very simple tooltip - may expand detail in the future...
2542 static gchar tmp_buf[32];
2543 g_snprintf (tmp_buf, sizeof(tmp_buf),
2545 g_hash_table_size (l->tracks));
2549 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2551 // Very simple tooltip - may expand detail in the future...
2552 static gchar tmp_buf[32];
2553 g_snprintf (tmp_buf, sizeof(tmp_buf),
2555 g_hash_table_size (l->routes));
2560 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2561 // Same tooltip for a route
2562 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2565 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2566 tr = g_hash_table_lookup ( l->tracks, sublayer );
2568 tr = g_hash_table_lookup ( l->routes, sublayer );
2571 // Could be a better way of handling strings - but this works...
2572 gchar time_buf1[20];
2573 gchar time_buf2[20];
2574 time_buf1[0] = '\0';
2575 time_buf2[0] = '\0';
2576 static gchar tmp_buf[100];
2577 // Compact info: Short date eg (11/20/99), duration and length
2578 // Hopefully these are the things that are most useful and so promoted into the tooltip
2579 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2580 // %x The preferred date representation for the current locale without the time.
2581 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2582 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2583 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2585 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2588 // Get length and consider the appropriate distance units
2589 gdouble tr_len = vik_track_get_length(tr);
2590 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2591 switch (dist_units) {
2592 case VIK_UNITS_DISTANCE_KILOMETRES:
2593 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2595 case VIK_UNITS_DISTANCE_MILES:
2596 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2605 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2607 // Very simple tooltip - may expand detail in the future...
2608 static gchar tmp_buf[32];
2609 g_snprintf (tmp_buf, sizeof(tmp_buf),
2611 g_hash_table_size (l->waypoints));
2615 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2617 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2618 // NB It's OK to return NULL
2623 return w->description;
2632 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2635 * set_statusbar_msg_info_trkpt:
2637 * Function to show track point information on the statusbar
2638 * Items displayed is controlled by the settings format code
2640 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2642 gchar *statusbar_format_code = NULL;
2643 gboolean need2free = FALSE;
2644 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2645 // Otherwise use default
2646 statusbar_format_code = g_strdup ( "KATDN" );
2650 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2651 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2655 g_free ( statusbar_format_code );
2659 * Function to show basic waypoint information on the statusbar
2661 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2664 switch (a_vik_get_units_height ()) {
2665 case VIK_UNITS_HEIGHT_FEET:
2666 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2669 //VIK_UNITS_HEIGHT_METRES:
2670 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2674 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2675 // one can easily use the current pointer position to see this if needed
2676 gchar *lat = NULL, *lon = NULL;
2677 static struct LatLon ll;
2678 vik_coord_to_latlon (&(wpt->coord), &ll);
2679 a_coords_latlon_to_string ( &ll, &lat, &lon );
2681 // Combine parts to make overall message
2684 // Add comment if available
2685 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2687 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2688 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2695 * General layer selection function, find out which bit is selected and take appropriate action
2697 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2700 l->current_wp = NULL;
2701 l->current_wp_id = NULL;
2702 trw_layer_cancel_current_tp ( l, FALSE );
2705 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2709 case VIK_TREEVIEW_TYPE_LAYER:
2711 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2712 /* Mark for redraw */
2717 case VIK_TREEVIEW_TYPE_SUBLAYER:
2721 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2723 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2724 /* Mark for redraw */
2728 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2730 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2731 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2732 /* Mark for redraw */
2736 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2738 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2739 /* Mark for redraw */
2743 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2745 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2746 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2747 /* Mark for redraw */
2751 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2753 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2754 /* Mark for redraw */
2758 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2760 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2762 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2763 // Show some waypoint info
2764 set_statusbar_msg_info_wpt ( l, wpt );
2765 /* Mark for redraw */
2772 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2781 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2786 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2791 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2796 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2798 return l->waypoints;
2801 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2803 return ! ( g_hash_table_size ( vtl->tracks ) ||
2804 g_hash_table_size ( vtl->routes ) ||
2805 g_hash_table_size ( vtl->waypoints ) );
2808 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2810 return vtl->tracks_visible;
2813 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2815 return vtl->routes_visible;
2818 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2820 return vtl->waypoints_visible;
2824 * ATM use a case sensitive find
2825 * Finds the first one
2827 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2829 if ( wp && wp->name )
2830 if ( ! strcmp ( wp->name, name ) )
2836 * Get waypoint by name - not guaranteed to be unique
2837 * Finds the first one
2839 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2841 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2845 * ATM use a case sensitive find
2846 * Finds the first one
2848 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2850 if ( trk && trk->name )
2851 if ( ! strcmp ( trk->name, name ) )
2857 * Get track by name - not guaranteed to be unique
2858 * Finds the first one
2860 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2862 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2866 * Get route by name - not guaranteed to be unique
2867 * Finds the first one
2869 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2871 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2874 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2876 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2877 maxmin[0].lat = trk->bbox.north;
2878 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2879 maxmin[1].lat = trk->bbox.south;
2880 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2881 maxmin[0].lon = trk->bbox.east;
2882 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2883 maxmin[1].lon = trk->bbox.west;
2886 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2888 // Continually reuse maxmin to find the latest maximum and minimum values
2889 // First set to waypoints bounds
2890 maxmin[0].lat = vtl->waypoints_bbox.north;
2891 maxmin[1].lat = vtl->waypoints_bbox.south;
2892 maxmin[0].lon = vtl->waypoints_bbox.east;
2893 maxmin[1].lon = vtl->waypoints_bbox.west;
2894 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2895 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2898 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2900 /* 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... */
2901 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2902 trw_layer_find_maxmin (vtl, maxmin);
2903 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2907 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2908 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2913 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2916 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2917 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2919 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2922 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2924 /* First set the center [in case previously viewing from elsewhere] */
2925 /* Then loop through zoom levels until provided positions are in view */
2926 /* This method is not particularly fast - but should work well enough */
2927 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2929 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2930 vik_viewport_set_center_coord ( vvp, &coord );
2932 /* Convert into definite 'smallest' and 'largest' positions */
2933 struct LatLon minmin;
2934 if ( maxmin[0].lat < maxmin[1].lat )
2935 minmin.lat = maxmin[0].lat;
2937 minmin.lat = maxmin[1].lat;
2939 struct LatLon maxmax;
2940 if ( maxmin[0].lon > maxmin[1].lon )
2941 maxmax.lon = maxmin[0].lon;
2943 maxmax.lon = maxmin[1].lon;
2945 /* Never zoom in too far - generally not that useful, as too close ! */
2946 /* Always recalculate the 'best' zoom level */
2948 vik_viewport_set_zoom ( vvp, zoom );
2950 gdouble min_lat, max_lat, min_lon, max_lon;
2951 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2952 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2953 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2954 /* NB I think the logic used in this test to determine if the bounds is within view
2955 fails if track goes across 180 degrees longitude.
2956 Hopefully that situation is not too common...
2957 Mind you viking doesn't really do edge locations to well anyway */
2958 if ( min_lat < minmin.lat &&
2959 max_lat > minmin.lat &&
2960 min_lon < maxmax.lon &&
2961 max_lon > maxmax.lon )
2962 /* Found within zoom level */
2967 vik_viewport_set_zoom ( vvp, zoom );
2971 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2973 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2974 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2975 trw_layer_find_maxmin (vtl, maxmin);
2976 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2979 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2984 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2986 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])) ) ) {
2987 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2990 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2993 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2995 GtkWidget *file_selector;
2997 gboolean failed = FALSE;
2998 file_selector = gtk_file_chooser_dialog_new (title,
3000 GTK_FILE_CHOOSER_ACTION_SAVE,
3001 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3002 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3004 gchar *cwd = g_get_current_dir();
3006 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3010 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3012 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3014 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3015 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
3017 gtk_widget_hide ( file_selector );
3018 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3019 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3020 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3025 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3027 gtk_widget_hide ( file_selector );
3028 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3029 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3030 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3035 gtk_widget_destroy ( file_selector );
3037 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3040 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3042 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3045 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3047 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3050 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3052 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3053 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3054 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3055 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3057 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3059 g_free ( auto_save_name );
3062 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3064 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3065 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3066 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3067 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3069 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3071 g_free ( auto_save_name );
3075 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3078 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3080 gchar *name_used = NULL;
3083 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3084 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3085 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3086 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3088 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3092 gchar *quoted_file = g_shell_quote ( name_used );
3093 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3094 g_free ( quoted_file );
3095 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3097 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3098 g_error_free ( err );
3102 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3103 //g_remove ( name_used );
3104 // Perhaps should be deleted when the program ends?
3105 // For now leave it to the user to delete it / use system temp cleanup methods.
3106 g_free ( name_used );
3110 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3112 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3115 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3117 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3120 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3122 gpointer layer_and_vlp[2];
3123 layer_and_vlp[0] = pass_along[0];
3124 layer_and_vlp[1] = pass_along[1];
3126 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3128 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3129 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3131 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3133 if ( !trk || !trk->name )
3136 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3137 gchar *auto_save_name = g_strdup ( trk->name );
3138 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3139 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3141 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
3143 g_free ( auto_save_name );
3147 VikWaypoint *wp; // input
3148 gpointer uuid; // output
3151 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3153 wpu_udata *user_data = udata;
3154 if ( wp == user_data->wp ) {
3155 user_data->uuid = id;
3161 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3163 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3164 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3165 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3167 GTK_RESPONSE_REJECT,
3169 GTK_RESPONSE_ACCEPT,
3172 GtkWidget *label, *entry;
3173 label = gtk_label_new(_("Waypoint Name:"));
3174 entry = gtk_entry_new();
3176 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3177 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3178 gtk_widget_show_all ( label );
3179 gtk_widget_show_all ( entry );
3181 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3183 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3185 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3186 // Find *first* wp with the given name
3187 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3190 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3193 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3194 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3196 // Find and select on the side panel
3201 // Hmmm, want key of it
3202 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3204 if ( wpf && udata.uuid ) {
3205 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3206 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3215 gtk_widget_destroy ( dia );
3218 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3220 gchar *default_name = highest_wp_number_get(vtl);
3221 VikWaypoint *wp = vik_waypoint_new();
3222 gchar *returned_name;
3224 wp->coord = *def_coord;
3226 // Attempt to auto set height if DEM data is available
3227 vik_waypoint_apply_dem_data ( wp, TRUE );
3229 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3231 if ( returned_name )
3234 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3235 g_free (default_name);
3236 g_free (returned_name);
3239 g_free (default_name);
3240 vik_waypoint_free(wp);
3244 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3246 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3247 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3248 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3249 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3250 VikViewport *vvp = vik_window_viewport(vw);
3252 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3253 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3254 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3255 trw_layer_calculate_bounds_waypoints ( vtl );
3256 vik_layers_panel_emit_update ( vlp );
3259 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3261 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3262 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3263 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3265 trw_layer_find_maxmin (vtl, maxmin);
3266 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3267 trw_layer_calculate_bounds_waypoints ( vtl );
3268 vik_layers_panel_emit_update ( vlp );
3271 #ifdef VIK_CONFIG_GEOTAG
3272 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3274 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3276 // Update directly - not changing the mtime
3277 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3280 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3282 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3285 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3289 * Use code in separate file for this feature as reasonably complex
3291 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3293 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3294 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3295 // Unset so can be reverified later if necessary
3296 vtl->has_verified_thumbnails = FALSE;
3298 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3304 static void trw_layer_geotagging ( gpointer lav[2] )
3306 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3307 // Unset so can be reverified later if necessary
3308 vtl->has_verified_thumbnails = FALSE;
3310 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3317 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3320 * Acquire into this TRW Layer straight from GPS Device
3322 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3324 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3325 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3326 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3327 VikViewport *vvp = vik_window_viewport(vw);
3329 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3330 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3334 * Acquire into this TRW Layer from Directions
3336 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3338 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3339 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3340 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3341 VikViewport *vvp = vik_window_viewport(vw);
3343 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3346 #ifdef VIK_CONFIG_OPENSTREETMAP
3348 * Acquire into this TRW Layer from OSM
3350 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3352 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3353 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3354 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3355 VikViewport *vvp = vik_window_viewport(vw);
3357 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3361 * Acquire into this TRW Layer from OSM for 'My' Traces
3363 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3365 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3366 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3367 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3368 VikViewport *vvp = vik_window_viewport(vw);
3370 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3374 #ifdef VIK_CONFIG_GEOCACHES
3376 * Acquire into this TRW Layer from Geocaching.com
3378 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3380 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3381 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3382 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3383 VikViewport *vvp = vik_window_viewport(vw);
3385 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3389 #ifdef VIK_CONFIG_GEOTAG
3391 * Acquire into this TRW Layer from images
3393 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3395 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3396 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3397 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3398 VikViewport *vvp = vik_window_viewport(vw);
3400 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3401 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3403 // Reverify thumbnails as they may have changed
3404 vtl->has_verified_thumbnails = FALSE;
3405 trw_layer_verify_thumbnails ( vtl, NULL );
3409 static void trw_layer_gps_upload ( gpointer lav[2] )
3411 gpointer pass_along[6];
3412 pass_along[0] = lav[0];
3413 pass_along[1] = lav[1];
3414 pass_along[2] = NULL; // No track - operate on the layer
3415 pass_along[3] = NULL;
3416 pass_along[4] = NULL;
3417 pass_along[5] = NULL;
3419 trw_layer_gps_upload_any ( pass_along );
3423 * If pass_along[3] is defined that this will upload just that track
3425 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3427 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3428 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3430 // May not actually get a track here as pass_along[2&3] can be null
3431 VikTrack *track = NULL;
3432 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3433 gboolean xfer_all = FALSE;
3435 if ( pass_along[2] ) {
3437 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3438 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3441 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3442 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3445 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3448 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3452 else if ( !pass_along[4] )
3453 xfer_all = TRUE; // i.e. whole layer
3455 if (track && !track->visible) {
3456 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3460 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3461 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3462 GTK_DIALOG_DESTROY_WITH_PARENT,
3464 GTK_RESPONSE_ACCEPT,
3466 GTK_RESPONSE_REJECT,
3469 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3470 GtkWidget *response_w = NULL;
3471 #if GTK_CHECK_VERSION (2, 20, 0)
3472 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3476 gtk_widget_grab_focus ( response_w );
3478 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3480 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3481 datasource_gps_clean_up ( dgs );
3482 gtk_widget_destroy ( dialog );
3486 // Get info from reused datasource dialog widgets
3487 gchar* protocol = datasource_gps_get_protocol ( dgs );
3488 gchar* port = datasource_gps_get_descriptor ( dgs );
3489 // NB don't free the above strings as they're references to values held elsewhere
3490 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3491 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3492 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3493 gboolean turn_off = datasource_gps_get_off ( dgs );
3495 gtk_widget_destroy ( dialog );
3497 // When called from the viewport - work the corresponding layerspanel:
3499 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3502 // Apply settings to transfer to the GPS device
3509 vik_layers_panel_get_viewport (vlp),
3518 * Acquire into this TRW Layer from any GPS Babel supported file
3520 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3522 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3523 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3524 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3525 VikViewport *vvp = vik_window_viewport(vw);
3527 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3530 static void trw_layer_new_wp ( gpointer lav[2] )
3532 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3533 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3534 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3535 instead return true if you want to update. */
3536 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 ) {
3537 trw_layer_calculate_bounds_waypoints ( vtl );
3538 vik_layers_panel_emit_update ( vlp );
3542 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3544 vtl->current_track = vik_track_new();
3545 vik_track_set_defaults ( vtl->current_track );
3546 vtl->current_track->visible = TRUE;
3547 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3548 // Create track with the preferred colour from the layer properties
3549 vtl->current_track->color = vtl->track_color;
3551 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3552 vtl->current_track->has_color = TRUE;
3553 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3556 static void trw_layer_new_track ( gpointer lav[2] )
3558 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3560 if ( ! vtl->current_track ) {
3561 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3562 new_track_create_common ( vtl, name );
3565 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3569 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3571 vtl->current_track = vik_track_new();
3572 vik_track_set_defaults ( vtl->current_track );
3573 vtl->current_track->visible = TRUE;
3574 vtl->current_track->is_route = TRUE;
3575 // By default make all routes red
3576 vtl->current_track->has_color = TRUE;
3577 gdk_color_parse ( "red", &vtl->current_track->color );
3578 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3581 static void trw_layer_new_route ( gpointer lav[2] )
3583 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3585 if ( ! vtl->current_track ) {
3586 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3587 new_route_create_common ( vtl, name );
3589 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3593 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3595 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3596 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3598 if ( g_hash_table_size (vtl->routes) > 0 ) {
3599 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3600 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3601 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3602 vik_layers_panel_emit_update ( vlp );
3607 static void trw_layer_finish_track ( gpointer lav[2] )
3609 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3610 vtl->current_track = NULL;
3611 vik_layer_emit_update ( VIK_LAYER(vtl) );
3614 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3616 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3617 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3619 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3620 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3621 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3622 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3623 vik_layers_panel_emit_update ( vlp );
3627 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3629 /* NB do not care if wp is visible or not */
3630 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3633 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3635 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3636 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3638 /* Only 1 waypoint - jump straight to it */
3639 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3640 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3641 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3643 /* If at least 2 waypoints - find center and then zoom to fit */
3644 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3646 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3647 maxmin[0].lat = vtl->waypoints_bbox.north;
3648 maxmin[1].lat = vtl->waypoints_bbox.south;
3649 maxmin[0].lon = vtl->waypoints_bbox.east;
3650 maxmin[1].lon = vtl->waypoints_bbox.west;
3651 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3654 vik_layers_panel_emit_update ( vlp );
3657 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3659 static gpointer pass_along[2];
3661 GtkWidget *export_submenu;
3662 pass_along[0] = vtl;
3663 pass_along[1] = vlp;
3665 item = gtk_menu_item_new();
3666 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3667 gtk_widget_show ( item );
3669 if ( vtl->current_track ) {
3670 if ( vtl->current_track->is_route )
3671 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3673 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3674 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3675 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3676 gtk_widget_show ( item );
3679 item = gtk_menu_item_new ();
3680 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3681 gtk_widget_show ( item );
3684 /* Now with icons */
3685 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3686 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3687 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3688 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3689 gtk_widget_show ( item );
3691 GtkWidget *view_submenu = gtk_menu_new();
3692 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3693 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3694 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3695 gtk_widget_show ( item );
3696 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3698 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3699 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3700 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3701 gtk_widget_show ( item );
3703 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3705 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3706 gtk_widget_show ( item );
3708 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3710 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3711 gtk_widget_show ( item );
3713 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3714 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3715 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3716 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3717 gtk_widget_show ( item );
3719 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3720 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3721 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3722 gtk_widget_show ( item );
3724 export_submenu = gtk_menu_new ();
3725 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3726 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3727 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3728 gtk_widget_show ( item );
3729 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3731 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3732 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3733 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3734 gtk_widget_show ( item );
3736 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3738 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3739 gtk_widget_show ( item );
3741 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3743 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3744 gtk_widget_show ( item );
3746 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3748 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3749 gtk_widget_show ( item );
3751 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3752 item = gtk_menu_item_new_with_mnemonic ( external1 );
3753 g_free ( external1 );
3754 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3755 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3756 gtk_widget_show ( item );
3758 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3759 item = gtk_menu_item_new_with_mnemonic ( external2 );
3760 g_free ( external2 );
3761 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3762 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3763 gtk_widget_show ( item );
3765 GtkWidget *new_submenu = gtk_menu_new();
3766 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3767 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3768 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3769 gtk_widget_show(item);
3770 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3772 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3773 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3775 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3776 gtk_widget_show ( item );
3778 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3779 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3781 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3782 gtk_widget_show ( item );
3783 // Make it available only when a new track *not* already in progress
3784 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3786 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3787 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3788 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3789 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3790 gtk_widget_show ( item );
3791 // Make it available only when a new track *not* already in progress
3792 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3794 #ifdef VIK_CONFIG_GEOTAG
3795 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3796 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3797 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3798 gtk_widget_show ( item );
3801 GtkWidget *acquire_submenu = gtk_menu_new ();
3802 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3803 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3804 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3805 gtk_widget_show ( item );
3806 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3808 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3809 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3810 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3811 gtk_widget_show ( item );
3813 /* FIXME: only add menu when at least a routing engine has support for Directions */
3814 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3815 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3816 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3817 gtk_widget_show ( item );
3819 #ifdef VIK_CONFIG_OPENSTREETMAP
3820 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3821 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3822 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3823 gtk_widget_show ( item );
3825 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3827 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3828 gtk_widget_show ( item );
3831 #ifdef VIK_CONFIG_GEONAMES
3832 GtkWidget *wikipedia_submenu = gtk_menu_new();
3833 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3834 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3835 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3836 gtk_widget_show(item);
3837 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3839 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3840 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3841 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3842 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3843 gtk_widget_show ( item );
3845 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3846 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3847 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3848 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3849 gtk_widget_show ( item );
3852 #ifdef VIK_CONFIG_GEOCACHES
3853 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3854 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3855 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3856 gtk_widget_show ( item );
3859 #ifdef VIK_CONFIG_GEOTAG
3860 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3861 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3862 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3863 gtk_widget_show ( item );
3866 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3867 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3868 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3869 gtk_widget_show ( item );
3871 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3873 GtkWidget *upload_submenu = gtk_menu_new ();
3874 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3876 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3877 gtk_widget_show ( item );
3878 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3880 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3883 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3884 gtk_widget_show ( item );
3886 #ifdef VIK_CONFIG_OPENSTREETMAP
3887 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3889 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3890 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3891 gtk_widget_show ( item );
3894 GtkWidget *delete_submenu = gtk_menu_new ();
3895 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3896 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3897 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3898 gtk_widget_show ( item );
3899 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3901 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3902 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3903 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3904 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3905 gtk_widget_show ( item );
3907 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3908 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3909 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3910 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3911 gtk_widget_show ( item );
3913 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3915 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3916 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3917 gtk_widget_show ( item );
3919 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3920 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3921 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3922 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3923 gtk_widget_show ( item );
3925 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3926 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3928 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3929 gtk_widget_show ( item );
3931 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3932 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3934 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3935 gtk_widget_show ( item );
3937 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3938 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3940 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3941 gtk_widget_show ( item );
3944 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3945 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3947 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3948 gtk_widget_show ( item );
3952 // Fake Waypoint UUIDs vi simple increasing integer
3953 static guint wp_uuid = 0;
3955 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3959 vik_waypoint_set_name (wp, name);
3961 if ( VIK_LAYER(vtl)->realized )
3963 // Do we need to create the sublayer:
3964 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3965 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3968 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3970 // Visibility column always needed for waypoints
3971 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 );
3973 // Actual setting of visibility dependent on the waypoint
3974 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3976 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3978 // Sort now as post_read is not called on a realized waypoint
3979 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
3982 highest_wp_number_add_wp(vtl, name);
3983 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3987 // Fake Track UUIDs vi simple increasing integer
3988 static guint tr_uuid = 0;
3990 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3994 vik_track_set_name (t, name);
3996 if ( VIK_LAYER(vtl)->realized )
3998 // Do we need to create the sublayer:
3999 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4000 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4003 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4004 // Visibility column always needed for tracks
4005 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 );
4007 // Actual setting of visibility dependent on the track
4008 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4010 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4012 // Sort now as post_read is not called on a realized track
4013 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4016 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4018 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
4021 // Fake Route UUIDs vi simple increasing integer
4022 static guint rt_uuid = 0;
4024 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4028 vik_track_set_name (t, name);
4030 if ( VIK_LAYER(vtl)->realized )
4032 // Do we need to create the sublayer:
4033 if ( g_hash_table_size (vtl->routes) == 0 ) {
4034 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4037 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4038 // Visibility column always needed for routes
4039 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 );
4040 // Actual setting of visibility dependent on the route
4041 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4043 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4045 // Sort now as post_read is not called on a realized route
4046 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4049 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4051 trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
4054 /* to be called whenever a track has been deleted or may have been changed. */
4055 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4057 if (vtl->current_tp_track == trk )
4058 trw_layer_cancel_current_tp ( vtl, FALSE );
4062 * Normally this is done to due the waypoint size preference having changed
4064 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4066 GHashTableIter iter;
4067 gpointer key, value;
4070 g_hash_table_iter_init ( &iter, vtl->waypoints );
4071 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4072 VikWaypoint *wp = VIK_WAYPOINT(value);
4074 // Reapply symbol setting to update the pixbuf
4075 gchar *tmp_symbol = g_strdup ( wp->symbol );
4076 vik_waypoint_set_symbol ( wp, tmp_symbol );
4077 g_free ( tmp_symbol );
4083 * trw_layer_new_unique_sublayer_name:
4085 * Allocates a unique new name
4087 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4090 gchar *newname = g_strdup(name);
4095 switch ( sublayer_type ) {
4096 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4097 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4099 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4100 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4103 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4106 // If found a name already in use try adding 1 to it and we try again
4108 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4110 newname = new_newname;
4113 } while ( id != NULL);
4118 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4120 // No more uniqueness of name forced when loading from a file
4121 // This now makes this function a little redunant as we just flow the parameters through
4122 vik_trw_layer_add_waypoint ( vtl, name, wp );
4125 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4127 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4128 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4129 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4130 vik_track_free ( tr );
4131 vtl->route_finder_append = FALSE; /* this means we have added it */
4134 // No more uniqueness of name forced when loading from a file
4136 vik_trw_layer_add_route ( vtl, name, tr );
4138 vik_trw_layer_add_track ( vtl, name, tr );
4140 if ( vtl->route_finder_check_added_track ) {
4141 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4142 vtl->route_finder_added_track = tr;
4147 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4149 *l = g_list_append(*l, id);
4153 * Move an item from one TRW layer to another TRW layer
4155 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4157 // TODO reconsider strategy when moving within layer (if anything...)
4158 gboolean rename = ( vtl_src != vtl_dest );
4162 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4163 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4167 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4169 newname = g_strdup ( trk->name );
4171 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4172 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4174 vik_trw_layer_delete_track ( vtl_src, trk );
4177 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4178 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4182 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4184 newname = g_strdup ( trk->name );
4186 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4187 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4189 vik_trw_layer_delete_route ( vtl_src, trk );
4192 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4193 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4197 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4199 newname = g_strdup ( wp->name );
4201 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4202 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4204 trw_layer_delete_waypoint ( vtl_src, wp );
4206 // Recalculate bounds even if not renamed as maybe dragged between layers
4207 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4208 trw_layer_calculate_bounds_waypoints ( vtl_src );
4212 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4214 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4215 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4217 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4218 GList *items = NULL;
4221 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4222 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4224 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4225 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4227 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4228 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4233 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4234 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4235 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4236 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4238 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4245 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4246 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4251 VikTrack *trk; // input
4252 gpointer uuid; // output
4255 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4257 trku_udata *user_data = udata;
4258 if ( trk == user_data->trk ) {
4259 user_data->uuid = id;
4265 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4267 gboolean was_visible = FALSE;
4269 if ( trk && trk->name ) {
4271 if ( trk == vtl->current_track ) {
4272 vtl->current_track = NULL;
4273 vtl->current_tp_track = NULL;
4274 vtl->current_tp_id = NULL;
4275 vtl->moving_tp = FALSE;
4278 was_visible = trk->visible;
4280 if ( trk == vtl->route_finder_current_track )
4281 vtl->route_finder_current_track = NULL;
4283 if ( trk == vtl->route_finder_added_track )
4284 vtl->route_finder_added_track = NULL;
4290 // Hmmm, want key of it
4291 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4293 if ( trkf && udata.uuid ) {
4294 /* could be current_tp, so we have to check */
4295 trw_layer_cancel_tps_of_track ( vtl, trk );
4297 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4300 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4301 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4302 g_hash_table_remove ( vtl->tracks, udata.uuid );
4304 // If last sublayer, then remove sublayer container
4305 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4306 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4314 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4316 gboolean was_visible = FALSE;
4318 if ( trk && trk->name ) {
4320 if ( trk == vtl->current_track ) {
4321 vtl->current_track = NULL;
4322 vtl->current_tp_track = NULL;
4323 vtl->current_tp_id = NULL;
4324 vtl->moving_tp = FALSE;
4327 was_visible = trk->visible;
4329 if ( trk == vtl->route_finder_current_track )
4330 vtl->route_finder_current_track = NULL;
4332 if ( trk == vtl->route_finder_added_track )
4333 vtl->route_finder_added_track = NULL;
4339 // Hmmm, want key of it
4340 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4342 if ( trkf && udata.uuid ) {
4343 /* could be current_tp, so we have to check */
4344 trw_layer_cancel_tps_of_track ( vtl, trk );
4346 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4349 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4350 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4351 g_hash_table_remove ( vtl->routes, udata.uuid );
4353 // If last sublayer, then remove sublayer container
4354 if ( g_hash_table_size (vtl->routes) == 0 ) {
4355 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4363 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4365 gboolean was_visible = FALSE;
4367 if ( wp && wp->name ) {
4369 if ( wp == vtl->current_wp ) {
4370 vtl->current_wp = NULL;
4371 vtl->current_wp_id = NULL;
4372 vtl->moving_wp = FALSE;
4375 was_visible = wp->visible;
4381 // Hmmm, want key of it
4382 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4384 if ( wpf && udata.uuid ) {
4385 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4388 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4389 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4391 highest_wp_number_remove_wp(vtl, wp->name);
4392 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4394 // If last sublayer, then remove sublayer container
4395 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4396 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4406 // Only for temporary use by trw_layer_delete_waypoint_by_name
4407 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4409 wpu_udata *user_data = udata;
4410 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4411 user_data->uuid = id;
4418 * Delete a waypoint by the given name
4419 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4420 * as there be multiple waypoints with the same name
4422 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4425 // Fake a waypoint with the given name
4426 udata.wp = vik_waypoint_new ();
4427 vik_waypoint_set_name (udata.wp, name);
4428 // Currently only the name is used in this waypoint find function
4431 // Hmmm, want key of it
4432 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4434 vik_waypoint_free (udata.wp);
4436 if ( wpf && udata.uuid )
4437 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4443 VikTrack *trk; // input
4444 gpointer uuid; // output
4447 // Only for temporary use by trw_layer_delete_track_by_name
4448 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4450 tpu_udata *user_data = udata;
4451 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4452 user_data->uuid = id;
4459 * Delete a track by the given name
4460 * NOTE: ATM this will delete the first encountered Track with the specified name
4461 * as there may be multiple tracks with the same name within the specified hash table
4463 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4466 // Fake a track with the given name
4467 udata.trk = vik_track_new ();
4468 vik_track_set_name (udata.trk, name);
4469 // Currently only the name is used in this waypoint find function
4472 // Hmmm, want key of it
4473 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4475 vik_track_free (udata.trk);
4477 if ( trkf && udata.uuid ) {
4478 // This could be a little better written...
4479 if ( vtl->tracks == ht_tracks )
4480 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4481 if ( vtl->routes == ht_tracks )
4482 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4489 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4491 vik_treeview_item_delete (vt, it );
4494 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4497 vtl->current_track = NULL;
4498 vtl->route_finder_current_track = NULL;
4499 vtl->route_finder_added_track = NULL;
4500 if (vtl->current_tp_track)
4501 trw_layer_cancel_current_tp(vtl, FALSE);
4503 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4504 g_hash_table_remove_all(vtl->routes_iters);
4505 g_hash_table_remove_all(vtl->routes);
4507 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4509 vik_layer_emit_update ( VIK_LAYER(vtl) );
4512 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4515 vtl->current_track = NULL;
4516 vtl->route_finder_current_track = NULL;
4517 vtl->route_finder_added_track = NULL;
4518 if (vtl->current_tp_track)
4519 trw_layer_cancel_current_tp(vtl, FALSE);
4521 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4522 g_hash_table_remove_all(vtl->tracks_iters);
4523 g_hash_table_remove_all(vtl->tracks);
4525 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4527 vik_layer_emit_update ( VIK_LAYER(vtl) );
4530 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4532 vtl->current_wp = NULL;
4533 vtl->current_wp_id = NULL;
4534 vtl->moving_wp = FALSE;
4536 highest_wp_number_reset(vtl);
4538 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4539 g_hash_table_remove_all(vtl->waypoints_iters);
4540 g_hash_table_remove_all(vtl->waypoints);
4542 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4544 vik_layer_emit_update ( VIK_LAYER(vtl) );
4547 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4549 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4550 // Get confirmation from the user
4551 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4552 _("Are you sure you want to delete all tracks in %s?"),
4553 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4554 vik_trw_layer_delete_all_tracks (vtl);
4557 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4559 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4560 // Get confirmation from the user
4561 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4562 _("Are you sure you want to delete all routes in %s?"),
4563 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4564 vik_trw_layer_delete_all_routes (vtl);
4567 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4569 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4570 // Get confirmation from the user
4571 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4572 _("Are you sure you want to delete all waypoints in %s?"),
4573 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4574 vik_trw_layer_delete_all_waypoints (vtl);
4577 static void trw_layer_delete_item ( gpointer pass_along[6] )
4579 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4580 gboolean was_visible = FALSE;
4581 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4583 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4584 if ( wp && wp->name ) {
4585 if ( GPOINTER_TO_INT ( pass_along[4]) )
4586 // Get confirmation from the user
4587 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4588 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4589 _("Are you sure you want to delete the waypoint \"%s\"?"),
4592 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4595 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4597 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4598 if ( trk && trk->name ) {
4599 if ( GPOINTER_TO_INT ( pass_along[4]) )
4600 // Get confirmation from the user
4601 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4602 _("Are you sure you want to delete the track \"%s\"?"),
4605 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4610 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4611 if ( trk && trk->name ) {
4612 if ( GPOINTER_TO_INT ( pass_along[4]) )
4613 // Get confirmation from the user
4614 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4615 _("Are you sure you want to delete the route \"%s\"?"),
4618 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4622 vik_layer_emit_update ( VIK_LAYER(vtl) );
4626 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4628 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4630 vik_waypoint_set_name ( wp, new_name );
4632 // Now update the treeview as well
4637 // Need key of it for treeview update
4638 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4640 if ( wpf && udataU.uuid ) {
4641 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4644 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4645 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4650 static void trw_layer_properties_item ( gpointer pass_along[7] )
4652 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4653 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4655 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4657 if ( wp && wp->name )
4659 gboolean updated = FALSE;
4660 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4662 trw_layer_waypoint_rename ( vtl, wp, new_name );
4664 if ( updated && pass_along[6] )
4665 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4667 if ( updated && VIK_LAYER(vtl)->visible )
4668 vik_layer_emit_update ( VIK_LAYER(vtl) );
4674 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4675 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4677 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4679 if ( tr && tr->name )
4681 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4684 pass_along[1], /* vlp */
4685 pass_along[5], /* vvp */
4686 pass_along[6], /* iter */
4693 * trw_layer_track_statistics:
4695 * Show track statistics.
4696 * ATM jump to the stats page in the properties
4697 * TODO: consider separating the stats into an individual dialog?
4699 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4701 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4703 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4704 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4706 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4708 if ( trk && trk->name ) {
4709 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4712 pass_along[1], // vlp
4713 pass_along[5], // vvp
4714 pass_along[6], // iter
4720 * Update the treeview of the track id - primarily to update the icon
4722 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4728 gpointer *trkf = NULL;
4729 if ( trk->is_route )
4730 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4732 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4734 if ( trkf && udata.uuid ) {
4736 GtkTreeIter *iter = NULL;
4737 if ( trk->is_route )
4738 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4740 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4743 // TODO: Make this a function
4744 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4745 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4746 ((trk->color.green & 0xff00) << 8) |
4747 (trk->color.blue & 0xff00);
4748 gdk_pixbuf_fill ( pixbuf, pixel );
4749 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4750 g_object_unref (pixbuf);
4757 Parameter 1 -> VikLayersPanel
4758 Parameter 2 -> VikLayer
4759 Parameter 3 -> VikViewport
4761 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4764 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4765 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4768 /* since vlp not set, vl & vvp should be valid instead! */
4770 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4771 vik_layer_emit_update ( VIK_LAYER(vl) );
4776 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4778 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4780 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4781 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4783 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4785 if ( track && track->trackpoints )
4786 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4789 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4791 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4793 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4794 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4796 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4798 if ( track && track->trackpoints )
4800 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4802 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4803 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4804 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4805 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4806 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4810 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4812 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4814 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4815 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4817 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4822 // Converting a track to a route can be a bit more complicated,
4823 // so give a chance to change our minds:
4824 if ( !trk->is_route &&
4825 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4826 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4828 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4829 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4834 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4837 trk_copy->is_route = !trk_copy->is_route;
4839 // ATM can't set name to self - so must create temporary copy
4840 gchar *name = g_strdup ( trk_copy->name );
4842 // Delete old one and then add new one
4843 if ( trk->is_route ) {
4844 vik_trw_layer_delete_route ( vtl, trk );
4845 vik_trw_layer_add_track ( vtl, name, trk_copy );
4848 // Extra route conversion bits...
4849 vik_track_merge_segments ( trk_copy );
4850 vik_track_to_routepoints ( trk_copy );
4852 vik_trw_layer_delete_track ( vtl, trk );
4853 vik_trw_layer_add_route ( vtl, name, trk_copy );
4857 // Update in case color of track / route changes when moving between sublayers
4858 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4861 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4863 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4865 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4866 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4868 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4871 vik_track_anonymize_times ( track );
4874 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4876 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4878 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4879 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4881 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4886 vtl->current_track = track;
4887 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);
4889 if ( track->trackpoints )
4890 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4894 * extend a track using route finder
4896 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4898 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4899 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4902 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4904 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4905 vtl->route_finder_coord = last_coord;
4906 vtl->route_finder_current_track = track;
4907 vtl->route_finder_started = TRUE;
4909 if ( track->trackpoints )
4910 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4917 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4919 // If have a vlp then perform a basic test to see if any DEM info available...
4921 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4923 if ( !g_list_length(dems) ) {
4924 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
4932 * apply_dem_data_common:
4934 * A common function for applying the DEM values and reporting the results.
4936 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
4938 if ( !trw_layer_dem_test ( vtl, vlp ) )
4941 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
4942 // Inform user how much was changed
4944 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4945 g_snprintf(str, 64, tmp_str, changed);
4946 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4949 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
4951 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4953 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4954 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4956 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4959 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
4962 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
4964 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4966 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4967 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4969 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4972 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
4978 * A common function for applying the elevation smoothing and reporting the results.
4980 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
4982 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
4983 // Inform user how much was changed
4985 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
4986 g_snprintf(str, 64, tmp_str, changed);
4987 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4993 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
4995 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4997 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4998 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5000 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5005 smooth_it ( vtl, track, FALSE );
5008 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5010 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5012 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5013 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5015 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5020 smooth_it ( vtl, track, TRUE );
5024 * Commonal helper function
5026 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5029 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5030 g_snprintf(str, 64, tmp_str, changed);
5031 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5034 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5036 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5037 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5039 if ( !trw_layer_dem_test ( vtl, vlp ) )
5043 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5045 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5047 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5051 GHashTableIter iter;
5052 gpointer key, value;
5054 g_hash_table_iter_init ( &iter, vtl->waypoints );
5055 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5056 VikWaypoint *wp = VIK_WAYPOINT(value);
5057 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5060 wp_changed_message ( vtl, changed );
5063 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5065 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5066 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5068 if ( !trw_layer_dem_test ( vtl, vlp ) )
5072 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5074 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5076 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5080 GHashTableIter iter;
5081 gpointer key, value;
5083 g_hash_table_iter_init ( &iter, vtl->waypoints );
5084 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5085 VikWaypoint *wp = VIK_WAYPOINT(value);
5086 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5089 wp_changed_message ( vtl, changed );
5092 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5094 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5096 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5097 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5099 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5104 GList *trps = track->trackpoints;
5107 trps = g_list_last(trps);
5108 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5111 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5113 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5115 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5116 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5118 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5123 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5126 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5129 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5131 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5133 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5134 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5136 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5141 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5144 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5147 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5149 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5151 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5152 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5154 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5159 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5162 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5166 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5168 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5170 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5172 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5173 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5175 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5177 if ( trk && trk->trackpoints )
5179 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5180 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5181 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5182 if ( pass_along[1] )
5183 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5185 vik_layer_emit_update ( VIK_LAYER(vtl) );
5190 * Refine the selected track/route with a routing engine.
5191 * The routing engine is selected by the user, when requestiong the job.
5193 static void trw_layer_route_refine ( gpointer pass_along[6] )
5195 static gint last_engine = 0;
5196 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5199 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5200 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5202 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5204 if ( trk && trk->trackpoints )
5206 /* Check size of the route */
5207 int nb = vik_track_get_tp_count(trk);
5209 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5210 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5211 GTK_MESSAGE_WARNING,
5212 GTK_BUTTONS_OK_CANCEL,
5213 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5215 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5216 gtk_widget_destroy ( dialog );
5217 if (response != GTK_RESPONSE_OK )
5220 /* Select engine from dialog */
5221 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5222 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5223 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5225 GTK_RESPONSE_REJECT,
5227 GTK_RESPONSE_ACCEPT,
5229 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5230 gtk_widget_show_all(label);
5232 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5234 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5235 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5236 gtk_widget_show_all(combo);
5238 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5240 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5242 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5244 /* Dialog validated: retrieve selected engine and do the job */
5245 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5246 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5249 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5251 /* Force saving track */
5252 /* FIXME: remove or rename this hack */
5253 vtl->route_finder_check_added_track = TRUE;
5256 vik_routing_engine_refine (routing, vtl, trk);
5258 /* FIXME: remove or rename this hack */
5259 if ( vtl->route_finder_added_track )
5260 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5262 vtl->route_finder_added_track = NULL;
5263 vtl->route_finder_check_added_track = FALSE;
5265 vik_layer_emit_update ( VIK_LAYER(vtl) );
5267 /* Restore cursor */
5268 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5270 gtk_widget_destroy ( dialog );
5274 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5276 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5277 trw_layer_tpwin_init ( vtl );
5280 /*************************************
5281 * merge/split by time routines
5282 *************************************/
5284 /* called for each key in track hash table.
5285 * If the current track has the same time stamp type, add it to the result,
5286 * except the one pointed by "exclude".
5287 * set exclude to NULL if there is no exclude to check.
5288 * Note that the result is in reverse (for performance reasons).
5293 gboolean with_timestamps;
5295 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5297 twt_udata *user_data = udata;
5298 VikTrackpoint *p1, *p2;
5300 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5304 if (VIK_TRACK(value)->trackpoints) {
5305 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5306 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5308 if ( user_data->with_timestamps ) {
5309 if (!p1->has_timestamp || !p2->has_timestamp) {
5314 // Don't add tracks with timestamps when getting non timestamp tracks
5315 if (p1->has_timestamp || p2->has_timestamp) {
5321 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5324 /* called for each key in track hash table. if original track user_data[1] is close enough
5325 * to the passed one, add it to list in user_data[0]
5327 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5330 VikTrackpoint *p1, *p2;
5331 VikTrack *trk = VIK_TRACK(value);
5333 GList **nearby_tracks = ((gpointer *)user_data)[0];
5334 GList *tpoints = ((gpointer *)user_data)[1];
5337 * detect reasons for not merging, and return
5338 * if no reason is found not to merge, then do it.
5341 // Exclude the original track from the compiled list
5342 if (trk->trackpoints == tpoints) {
5346 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5347 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5349 if (trk->trackpoints) {
5350 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5351 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5353 if (!p1->has_timestamp || !p2->has_timestamp) {
5354 //g_print("no timestamp\n");
5358 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5359 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5360 if (! (abs(t1 - p2->timestamp) < threshold ||
5362 abs(p1->timestamp - t2) < threshold)
5369 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5372 /* comparison function used to sort tracks; a and b are hash table keys */
5373 /* Not actively used - can be restored if needed
5374 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5376 GHashTable *tracks = user_data;
5379 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5380 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5382 if (t1 < t2) return -1;
5383 if (t1 > t2) return 1;
5388 /* comparison function used to sort trackpoints */
5389 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5391 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5393 if (t1 < t2) return -1;
5394 if (t1 > t2) return 1;
5399 * comparison function which can be used to sort tracks or waypoints by name
5401 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5403 const gchar* namea = (const gchar*) a;
5404 const gchar* nameb = (const gchar*) b;
5405 if ( namea == NULL || nameb == NULL)
5408 // Same sort method as used in the vik_treeview_*_alphabetize functions
5409 return strcmp ( namea, nameb );
5413 * Attempt to merge selected track with other tracks specified by the user
5414 * Tracks to merge with must be of the same 'type' as the selected track -
5415 * either all with timestamps, or all without timestamps
5417 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5419 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5420 GList *other_tracks = NULL;
5421 GHashTable *ght_tracks;
5422 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5423 ght_tracks = vtl->routes;
5425 ght_tracks = vtl->tracks;
5427 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5432 if ( !track->trackpoints )
5436 udata.result = &other_tracks;
5437 udata.exclude = track->trackpoints;
5438 // Allow merging with 'similar' time type time tracks
5439 // i.e. either those times, or those without
5440 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5442 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5443 other_tracks = g_list_reverse(other_tracks);
5445 if ( !other_tracks ) {
5446 if ( udata.with_timestamps )
5447 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5449 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5453 // Sort alphabetically for user presentation
5454 // Convert into list of names for usage with dialog function
5455 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5456 GList *other_tracks_names = NULL;
5457 GList *iter = g_list_first ( other_tracks );
5459 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5460 iter = g_list_next ( iter );
5463 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5465 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5469 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5470 g_list_free(other_tracks);
5471 g_list_free(other_tracks_names);
5476 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5477 VikTrack *merge_track;
5478 if ( track->is_route )
5479 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5481 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5484 vik_track_steal_and_append_trackpoints ( track, merge_track );
5485 if ( track->is_route )
5486 vik_trw_layer_delete_route (vtl, merge_track);
5488 vik_trw_layer_delete_track (vtl, merge_track);
5489 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5492 for (l = merge_list; l != NULL; l = g_list_next(l))
5494 g_list_free(merge_list);
5496 vik_layer_emit_update( VIK_LAYER(vtl) );
5500 // c.f. trw_layer_sorted_track_id_by_name_list
5501 // but don't add the specified track to the list (normally current track)
5502 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5504 twt_udata *user_data = udata;
5507 if (trk->trackpoints == user_data->exclude) {
5511 // Sort named list alphabetically
5512 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5516 * Join - this allows combining 'tracks' and 'track routes'
5517 * i.e. doesn't care about whether tracks have consistent timestamps
5518 * ATM can only append one track at a time to the currently selected track
5520 static void trw_layer_append_track ( gpointer pass_along[6] )
5523 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5525 GHashTable *ght_tracks;
5526 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5527 ght_tracks = vtl->routes;
5529 ght_tracks = vtl->tracks;
5531 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5536 GList *other_tracks_names = NULL;
5538 // Sort alphabetically for user presentation
5539 // Convert into list of names for usage with dialog function
5540 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5542 udata.result = &other_tracks_names;
5543 udata.exclude = trk->trackpoints;
5545 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5547 // Note the limit to selecting one track only
5548 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5549 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5550 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5553 trk->is_route ? _("Append Route"): _("Append Track"),
5554 trk->is_route ? _("Select the route to append after the current route") :
5555 _("Select the track to append after the current track") );
5557 g_list_free(other_tracks_names);
5559 // It's a list, but shouldn't contain more than one other track!
5560 if ( append_list ) {
5562 for (l = append_list; l != NULL; l = g_list_next(l)) {
5563 // TODO: at present this uses the first track found by name,
5564 // which with potential multiple same named tracks may not be the one selected...
5565 VikTrack *append_track;
5566 if ( trk->is_route )
5567 append_track = vik_trw_layer_get_route ( vtl, l->data );
5569 append_track = vik_trw_layer_get_track ( vtl, l->data );
5571 if ( append_track ) {
5572 vik_track_steal_and_append_trackpoints ( trk, append_track );
5573 if ( trk->is_route )
5574 vik_trw_layer_delete_route (vtl, append_track);
5576 vik_trw_layer_delete_track (vtl, append_track);
5579 for (l = append_list; l != NULL; l = g_list_next(l))
5581 g_list_free(append_list);
5583 vik_layer_emit_update( VIK_LAYER(vtl) );
5588 * Very similar to trw_layer_append_track for joining
5589 * but this allows selection from the 'other' list
5590 * If a track is selected, then is shows routes and joins the selected one
5591 * If a route is selected, then is shows tracks and joins the selected one
5593 static void trw_layer_append_other ( gpointer pass_along[6] )
5596 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5598 GHashTable *ght_mykind, *ght_others;
5599 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5600 ght_mykind = vtl->routes;
5601 ght_others = vtl->tracks;
5604 ght_mykind = vtl->tracks;
5605 ght_others = vtl->routes;
5608 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5613 GList *other_tracks_names = NULL;
5615 // Sort alphabetically for user presentation
5616 // Convert into list of names for usage with dialog function
5617 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5619 udata.result = &other_tracks_names;
5620 udata.exclude = trk->trackpoints;
5622 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5624 // Note the limit to selecting one track only
5625 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5626 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5627 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5630 trk->is_route ? _("Append Track"): _("Append Route"),
5631 trk->is_route ? _("Select the track to append after the current route") :
5632 _("Select the route to append after the current track") );
5634 g_list_free(other_tracks_names);
5636 // It's a list, but shouldn't contain more than one other track!
5637 if ( append_list ) {
5639 for (l = append_list; l != NULL; l = g_list_next(l)) {
5640 // TODO: at present this uses the first track found by name,
5641 // which with potential multiple same named tracks may not be the one selected...
5643 // Get FROM THE OTHER TYPE list
5644 VikTrack *append_track;
5645 if ( trk->is_route )
5646 append_track = vik_trw_layer_get_track ( vtl, l->data );
5648 append_track = vik_trw_layer_get_route ( vtl, l->data );
5650 if ( append_track ) {
5652 if ( !append_track->is_route &&
5653 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5654 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5656 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5657 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5658 vik_track_merge_segments ( append_track );
5659 vik_track_to_routepoints ( append_track );
5666 vik_track_steal_and_append_trackpoints ( trk, append_track );
5668 // Delete copied which is FROM THE OTHER TYPE list
5669 if ( trk->is_route )
5670 vik_trw_layer_delete_track (vtl, append_track);
5672 vik_trw_layer_delete_route (vtl, append_track);
5675 for (l = append_list; l != NULL; l = g_list_next(l))
5677 g_list_free(append_list);
5678 vik_layer_emit_update( VIK_LAYER(vtl) );
5682 /* merge by segments */
5683 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5685 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5686 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5687 guint segments = vik_track_merge_segments ( trk );
5688 // NB currently no need to redraw as segments not actually shown on the display
5689 // However inform the user of what happened:
5691 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5692 g_snprintf(str, 64, tmp_str, segments);
5693 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5696 /* merge by time routine */
5697 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5699 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5703 GList *tracks_with_timestamp = NULL;
5704 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5705 if (orig_trk->trackpoints &&
5706 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5707 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5712 udata.result = &tracks_with_timestamp;
5713 udata.exclude = orig_trk->trackpoints;
5714 udata.with_timestamps = TRUE;
5715 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5716 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5718 if (!tracks_with_timestamp) {
5719 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5722 g_list_free(tracks_with_timestamp);
5724 static guint threshold_in_minutes = 1;
5725 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5726 _("Merge Threshold..."),
5727 _("Merge when time between tracks less than:"),
5728 &threshold_in_minutes)) {
5732 // keep attempting to merge all tracks until no merges within the time specified is possible
5733 gboolean attempt_merge = TRUE;
5734 GList *nearby_tracks = NULL;
5736 static gpointer params[3];
5738 while ( attempt_merge ) {
5740 // Don't try again unless tracks have changed
5741 attempt_merge = FALSE;
5743 trps = orig_trk->trackpoints;
5747 if (nearby_tracks) {
5748 g_list_free(nearby_tracks);
5749 nearby_tracks = NULL;
5752 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5753 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5755 /* g_print("Original track times: %d and %d\n", t1, t2); */
5756 params[0] = &nearby_tracks;
5757 params[1] = (gpointer)trps;
5758 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5760 /* get a list of adjacent-in-time tracks */
5761 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5764 GList *l = nearby_tracks;
5767 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5768 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5770 t1 = get_first_trackpoint(l)->timestamp;
5771 t2 = get_last_trackpoint(l)->timestamp;
5772 #undef get_first_trackpoint
5773 #undef get_last_trackpoint
5774 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5777 /* remove trackpoints from merged track, delete track */
5778 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5779 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5781 // Tracks have changed, therefore retry again against all the remaining tracks
5782 attempt_merge = TRUE;
5787 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5790 g_list_free(nearby_tracks);
5792 vik_layer_emit_update( VIK_LAYER(vtl) );
5796 * Split a track at the currently selected trackpoint
5798 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5800 if ( !vtl->current_tpl )
5803 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5804 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5806 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5807 GList *newglist = g_list_alloc ();
5808 newglist->prev = NULL;
5809 newglist->next = vtl->current_tpl->next;
5810 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5811 tr->trackpoints = newglist;
5813 vtl->current_tpl->next->prev = newglist; /* end old track here */
5814 vtl->current_tpl->next = NULL;
5816 // Bounds of the selected track changed due to the split
5817 vik_track_calculate_bounds ( vtl->current_tp_track );
5819 vtl->current_tpl = newglist; /* change tp to first of new track. */
5820 vtl->current_tp_track = tr;
5823 vik_trw_layer_add_route ( vtl, name, tr );
5825 vik_trw_layer_add_track ( vtl, name, tr );
5827 // Bounds of the new track created by the split
5828 vik_track_calculate_bounds ( tr );
5834 // Also need id of newly created track
5837 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5839 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5841 if ( trkf && udata.uuid )
5842 vtl->current_tp_id = udata.uuid;
5844 vtl->current_tp_id = NULL;
5846 vik_layer_emit_update(VIK_LAYER(vtl));
5852 /* split by time routine */
5853 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5855 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5856 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5857 GList *trps = track->trackpoints;
5859 GList *newlists = NULL;
5860 GList *newtps = NULL;
5861 static guint thr = 1;
5868 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5869 _("Split Threshold..."),
5870 _("Split when time between trackpoints exceeds:"),
5875 /* iterate through trackpoints, and copy them into new lists without touching original list */
5876 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5880 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5882 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5885 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5886 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5887 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5889 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5894 if (ts - prev_ts > thr*60) {
5895 /* flush accumulated trackpoints into new list */
5896 newlists = g_list_append(newlists, g_list_reverse(newtps));
5900 /* accumulate trackpoint copies in newtps, in reverse order */
5901 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5903 iter = g_list_next(iter);
5906 newlists = g_list_append(newlists, g_list_reverse(newtps));
5909 /* put lists of trackpoints into tracks */
5911 // Only bother updating if the split results in new tracks
5912 if (g_list_length (newlists) > 1) {
5917 tr = vik_track_copy ( track, FALSE );
5918 tr->trackpoints = (GList *)(iter->data);
5920 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5921 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5922 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5923 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5924 g_free ( new_tr_name );
5925 vik_track_calculate_bounds ( tr );
5926 iter = g_list_next(iter);
5928 // Remove original track and then update the display
5929 vik_trw_layer_delete_track (vtl, track);
5930 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5932 g_list_free(newlists);
5936 * Split a track by the number of points as specified by the user
5938 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5940 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5942 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5943 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5945 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5950 // Check valid track
5951 GList *trps = track->trackpoints;
5955 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5956 _("Split Every Nth Point"),
5957 _("Split on every Nth point:"),
5958 250, // Default value as per typical limited track capacity of various GPS devices
5962 // Was a valid number returned?
5968 GList *newlists = NULL;
5969 GList *newtps = NULL;
5974 /* accumulate trackpoint copies in newtps, in reverse order */
5975 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5977 if (count >= points) {
5978 /* flush accumulated trackpoints into new list */
5979 newlists = g_list_append(newlists, g_list_reverse(newtps));
5983 iter = g_list_next(iter);
5986 // If there is a remaining chunk put that into the new split list
5987 // This may well be the whole track if no split points were encountered
5989 newlists = g_list_append(newlists, g_list_reverse(newtps));
5992 /* put lists of trackpoints into tracks */
5994 // Only bother updating if the split results in new tracks
5995 if (g_list_length (newlists) > 1) {
6000 tr = vik_track_copy ( track, FALSE );
6001 tr->trackpoints = (GList *)(iter->data);
6003 if ( track->is_route ) {
6004 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6005 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6008 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6009 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6011 g_free ( new_tr_name );
6012 vik_track_calculate_bounds ( tr );
6014 iter = g_list_next(iter);
6016 // Remove original track and then update the display
6017 if ( track->is_route )
6018 vik_trw_layer_delete_route (vtl, track);
6020 vik_trw_layer_delete_track (vtl, track);
6021 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6023 g_list_free(newlists);
6027 * Split a track at the currently selected trackpoint
6029 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6031 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6032 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6033 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6037 * Split a track by its segments
6038 * Routes do not have segments so don't call this for routes
6040 static void trw_layer_split_segments ( gpointer pass_along[6] )
6042 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6043 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6050 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6053 for ( i = 0; i < ntracks; i++ ) {
6055 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6056 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6057 g_free ( new_tr_name );
6062 // Remove original track
6063 vik_trw_layer_delete_track ( vtl, trk );
6064 vik_layer_emit_update ( VIK_LAYER(vtl) );
6067 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6070 /* end of split/merge routines */
6073 * Delete adjacent track points at the same position
6074 * AKA Delete Dulplicates on the Properties Window
6076 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6078 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6080 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6081 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6083 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6088 gulong removed = vik_track_remove_dup_points ( trk );
6090 // Track has been updated so update tps:
6091 trw_layer_cancel_tps_of_track ( vtl, trk );
6093 // Inform user how much was deleted as it's not obvious from the normal view
6095 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6096 g_snprintf(str, 64, tmp_str, removed);
6097 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6099 vik_layer_emit_update ( VIK_LAYER(vtl) );
6103 * Delete adjacent track points with the same timestamp
6104 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6106 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6108 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6110 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6111 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6113 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6118 gulong removed = vik_track_remove_same_time_points ( trk );
6120 // Track has been updated so update tps:
6121 trw_layer_cancel_tps_of_track ( vtl, trk );
6123 // Inform user how much was deleted as it's not obvious from the normal view
6125 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6126 g_snprintf(str, 64, tmp_str, removed);
6127 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6129 vik_layer_emit_update ( VIK_LAYER(vtl) );
6135 static void trw_layer_reverse ( gpointer pass_along[6] )
6137 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6139 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6140 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6142 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6147 vik_track_reverse ( track );
6149 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6153 * Similar to trw_layer_enum_item, but this uses a sorted method
6156 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6158 GList **list = (GList**)udata;
6159 // *list = g_list_prepend(*all, key); //unsorted method
6160 // Sort named list alphabetically
6161 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6166 * Now Waypoint specific sort
6168 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6170 GList **list = (GList**)udata;
6171 // Sort named list alphabetically
6172 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6176 * Track specific sort
6178 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6180 GList **list = (GList**)udata;
6181 // Sort named list alphabetically
6182 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6187 gboolean has_same_track_name;
6188 const gchar *same_track_name;
6189 } same_track_name_udata;
6191 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6193 const gchar* namea = (const gchar*) aa;
6194 const gchar* nameb = (const gchar*) bb;
6197 gint result = strcmp ( namea, nameb );
6199 if ( result == 0 ) {
6200 // Found two names the same
6201 same_track_name_udata *user_data = udata;
6202 user_data->has_same_track_name = TRUE;
6203 user_data->same_track_name = namea;
6206 // Leave ordering the same
6211 * Find out if any tracks have the same name in this hash table
6213 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6215 // Sort items by name, then compare if any next to each other are the same
6217 GList *track_names = NULL;
6218 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6221 if ( ! track_names )
6224 same_track_name_udata udata;
6225 udata.has_same_track_name = FALSE;
6227 // Use sort routine to traverse list comparing items
6228 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6229 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6230 // Still no tracks...
6234 return udata.has_same_track_name;
6238 * Force unqiue track names for the track table specified
6239 * Note the panel is a required parameter to enable the update of the names displayed
6240 * Specify if on tracks or else on routes
6242 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6244 // . Search list for an instance of repeated name
6245 // . get track of this name
6246 // . create new name
6247 // . rename track & update equiv. treeview iter
6248 // . repeat until all different
6250 same_track_name_udata udata;
6252 GList *track_names = NULL;
6253 udata.has_same_track_name = FALSE;
6254 udata.same_track_name = NULL;
6256 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6259 if ( ! track_names )
6262 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6264 // Still no tracks...
6265 if ( ! dummy_list1 )
6268 while ( udata.has_same_track_name ) {
6270 // Find a track with the same name
6273 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6275 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6279 g_critical("Houston, we've had a problem.");
6280 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6281 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6286 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6287 vik_track_set_name ( trk, newname );
6293 // Need want key of it for treeview update
6294 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6296 if ( trkf && udataU.uuid ) {
6300 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6302 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6305 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6307 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6309 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6313 // Start trying to find same names again...
6315 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6316 udata.has_same_track_name = FALSE;
6317 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6319 // No tracks any more - give up searching
6320 if ( ! dummy_list2 )
6321 udata.has_same_track_name = FALSE;
6325 vik_layers_panel_emit_update ( vlp );
6328 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6330 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6333 switch (GPOINTER_TO_INT (pass_along[2])) {
6334 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6335 iter = &(vtl->tracks_iter);
6336 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6338 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6339 iter = &(vtl->routes_iter);
6340 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6342 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6343 iter = &(vtl->waypoints_iter);
6344 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6348 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6351 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6353 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6356 switch (GPOINTER_TO_INT (pass_along[2])) {
6357 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6358 iter = &(vtl->tracks_iter);
6359 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6361 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6362 iter = &(vtl->routes_iter);
6363 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6365 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6366 iter = &(vtl->waypoints_iter);
6367 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6371 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6377 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6379 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6382 // Ensure list of track names offered is unique
6383 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6384 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6385 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6386 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6392 // Sort list alphabetically for better presentation
6393 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6396 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6400 // Get list of items to delete from the user
6401 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6404 _("Delete Selection"),
6405 _("Select tracks to delete"));
6408 // Delete requested tracks
6409 // since specificly requested, IMHO no need for extra confirmation
6410 if ( delete_list ) {
6412 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6413 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6414 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6416 g_list_free(delete_list);
6417 vik_layer_emit_update( VIK_LAYER(vtl) );
6424 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6426 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6429 // Ensure list of track names offered is unique
6430 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6431 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6432 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6433 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6439 // Sort list alphabetically for better presentation
6440 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6443 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6447 // Get list of items to delete from the user
6448 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6451 _("Delete Selection"),
6452 _("Select routes to delete") );
6455 // Delete requested routes
6456 // since specificly requested, IMHO no need for extra confirmation
6457 if ( delete_list ) {
6459 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6460 // This deletes first route it finds of that name (but uniqueness is enforced above)
6461 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6463 g_list_free(delete_list);
6464 vik_layer_emit_update( VIK_LAYER(vtl) );
6469 gboolean has_same_waypoint_name;
6470 const gchar *same_waypoint_name;
6471 } same_waypoint_name_udata;
6473 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6475 const gchar* namea = (const gchar*) aa;
6476 const gchar* nameb = (const gchar*) bb;
6479 gint result = strcmp ( namea, nameb );
6481 if ( result == 0 ) {
6482 // Found two names the same
6483 same_waypoint_name_udata *user_data = udata;
6484 user_data->has_same_waypoint_name = TRUE;
6485 user_data->same_waypoint_name = namea;
6488 // Leave ordering the same
6493 * Find out if any waypoints have the same name in this layer
6495 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6497 // Sort items by name, then compare if any next to each other are the same
6499 GList *waypoint_names = NULL;
6500 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6503 if ( ! waypoint_names )
6506 same_waypoint_name_udata udata;
6507 udata.has_same_waypoint_name = FALSE;
6509 // Use sort routine to traverse list comparing items
6510 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6511 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6512 // Still no waypoints...
6516 return udata.has_same_waypoint_name;
6520 * Force unqiue waypoint names for this layer
6521 * Note the panel is a required parameter to enable the update of the names displayed
6523 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6525 // . Search list for an instance of repeated name
6526 // . get waypoint of this name
6527 // . create new name
6528 // . rename waypoint & update equiv. treeview iter
6529 // . repeat until all different
6531 same_waypoint_name_udata udata;
6533 GList *waypoint_names = NULL;
6534 udata.has_same_waypoint_name = FALSE;
6535 udata.same_waypoint_name = NULL;
6537 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6540 if ( ! waypoint_names )
6543 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6545 // Still no waypoints...
6546 if ( ! dummy_list1 )
6549 while ( udata.has_same_waypoint_name ) {
6551 // Find a waypoint with the same name
6552 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6556 g_critical("Houston, we've had a problem.");
6557 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6558 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6563 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6565 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6567 // Start trying to find same names again...
6568 waypoint_names = NULL;
6569 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6570 udata.has_same_waypoint_name = FALSE;
6571 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6573 // No waypoints any more - give up searching
6574 if ( ! dummy_list2 )
6575 udata.has_same_waypoint_name = FALSE;
6579 vik_layers_panel_emit_update ( vlp );
6585 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6587 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6590 // Ensure list of waypoint names offered is unique
6591 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6592 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6593 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6594 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6600 // Sort list alphabetically for better presentation
6601 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6603 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6607 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6609 // Get list of items to delete from the user
6610 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6613 _("Delete Selection"),
6614 _("Select waypoints to delete"));
6617 // Delete requested waypoints
6618 // since specificly requested, IMHO no need for extra confirmation
6619 if ( delete_list ) {
6621 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6622 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6623 trw_layer_delete_waypoint_by_name (vtl, l->data);
6625 g_list_free(delete_list);
6627 trw_layer_calculate_bounds_waypoints ( vtl );
6628 vik_layer_emit_update( VIK_LAYER(vtl) );
6636 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6638 vik_treeview_item_toggle_visible ( vt, it );
6644 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6646 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6652 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6654 wp->visible = GPOINTER_TO_INT (on_off);
6660 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6662 wp->visible = !wp->visible;
6668 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6670 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6671 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6672 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6673 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6675 vik_layer_emit_update ( VIK_LAYER(vtl) );
6681 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6683 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6684 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6685 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6686 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6688 vik_layer_emit_update ( VIK_LAYER(vtl) );
6694 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6696 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6697 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6698 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6700 vik_layer_emit_update ( VIK_LAYER(vtl) );
6706 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6708 trk->visible = GPOINTER_TO_INT (on_off);
6714 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6716 trk->visible = !trk->visible;
6722 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6724 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6725 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6726 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6727 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6729 vik_layer_emit_update ( VIK_LAYER(vtl) );
6735 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6737 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6738 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6739 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6740 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6742 vik_layer_emit_update ( VIK_LAYER(vtl) );
6748 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6750 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6751 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6752 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6754 vik_layer_emit_update ( VIK_LAYER(vtl) );
6760 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6762 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6763 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6764 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6765 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6767 vik_layer_emit_update ( VIK_LAYER(vtl) );
6773 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6775 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6776 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6777 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6778 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6780 vik_layer_emit_update ( VIK_LAYER(vtl) );
6786 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6788 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6789 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6790 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6792 vik_layer_emit_update ( VIK_LAYER(vtl) );
6796 * trw_layer_analyse_close:
6798 * Stuff to do on dialog closure
6800 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6802 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6803 gtk_widget_destroy ( dialog );
6804 vtl->tracks_analysis_dialog = NULL;
6808 * trw_layer_analyse_create_list:
6810 * Create the latest list of tracks with the associated layer(s)
6811 * Although this will always be from a single layer here
6813 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6815 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6816 GList *tracks = NULL;
6817 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6818 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6820 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6822 GList *tracks_and_layers = NULL;
6823 // build tracks_and_layers list
6824 tracks = g_list_first ( tracks );
6826 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6827 vtdl->trk = VIK_TRACK(tracks->data);
6829 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6830 tracks = g_list_next ( tracks );
6833 return tracks_and_layers;
6836 static void trw_layer_tracks_stats ( gpointer lav[2] )
6838 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6839 // There can only be one!
6840 if ( vtl->tracks_analysis_dialog )
6843 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6844 VIK_LAYER(vtl)->name,
6846 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6847 trw_layer_analyse_create_list,
6848 trw_layer_analyse_close );
6854 static void trw_layer_routes_stats ( gpointer lav[2] )
6856 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6857 // There can only be one!
6858 if ( vtl->tracks_analysis_dialog )
6861 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6862 VIK_LAYER(vtl)->name,
6864 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6865 trw_layer_analyse_create_list,
6866 trw_layer_analyse_close );
6869 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6871 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6873 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6876 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6878 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6881 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6882 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6886 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6888 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6891 if ( !strncmp(wp->comment, "http", 4) ) {
6892 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6893 } else if ( !strncmp(wp->description, "http", 4) ) {
6894 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6898 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6900 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6902 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6904 // No actual change to the name supplied
6906 if (strcmp(newname, wp->name) == 0 )
6909 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6912 // An existing waypoint has been found with the requested name
6913 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6914 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6919 // Update WP name and refresh the treeview
6920 vik_waypoint_set_name (wp, newname);
6922 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6923 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6925 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6930 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6932 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6934 // No actual change to the name supplied
6936 if (strcmp(newname, trk->name) == 0)
6939 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6942 // An existing track has been found with the requested name
6943 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6944 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6948 // Update track name and refresh GUI parts
6949 vik_track_set_name (trk, newname);
6951 // Update any subwindows that could be displaying this track which has changed name
6952 // Only one Track Edit Window
6953 if ( l->current_tp_track == trk && l->tpwin ) {
6954 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6956 // Property Dialog of the track
6957 vik_trw_layer_propwin_update ( trk );
6959 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6960 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6962 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6967 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6969 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6971 // No actual change to the name supplied
6973 if (strcmp(newname, trk->name) == 0)
6976 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6979 // An existing track has been found with the requested name
6980 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6981 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6985 // Update track name and refresh GUI parts
6986 vik_track_set_name (trk, newname);
6988 // Update any subwindows that could be displaying this track which has changed name
6989 // Only one Track Edit Window
6990 if ( l->current_tp_track == trk && l->tpwin ) {
6991 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6993 // Property Dialog of the track
6994 vik_trw_layer_propwin_update ( trk );
6996 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6997 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6999 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7006 static gboolean is_valid_geocache_name ( gchar *str )
7008 gint len = strlen ( str );
7009 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]));
7012 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7014 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7015 a_acquire_set_filter_track ( trk );
7018 #ifdef VIK_CONFIG_GOOGLE
7019 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7021 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7022 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7025 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7027 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7029 gchar *escaped = uri_escape ( tr->comment );
7030 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7031 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7038 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7039 /* viewpoint is now available instead */
7040 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7042 static gpointer pass_along[8];
7044 gboolean rv = FALSE;
7047 pass_along[1] = vlp;
7048 pass_along[2] = GINT_TO_POINTER (subtype);
7049 pass_along[3] = sublayer;
7050 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7051 pass_along[5] = vvp;
7052 pass_along[6] = iter;
7053 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7055 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7059 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7061 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7062 gtk_widget_show ( item );
7064 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7065 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7066 if (tr && tr->property_dialog)
7067 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7069 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7070 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7071 if (tr && tr->property_dialog)
7072 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7075 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7077 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7078 gtk_widget_show ( item );
7080 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7082 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7083 gtk_widget_show ( item );
7085 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7087 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7088 gtk_widget_show ( item );
7090 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7092 gboolean separator_created = FALSE;
7094 /* could be a right-click using the tool */
7095 if ( vlp != NULL ) {
7096 item = gtk_menu_item_new ();
7097 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7098 gtk_widget_show ( item );
7100 separator_created = TRUE;
7102 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7103 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7105 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7106 gtk_widget_show ( item );
7109 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7111 if ( wp && wp->name ) {
7112 if ( is_valid_geocache_name ( wp->name ) ) {
7114 if ( !separator_created ) {
7115 item = gtk_menu_item_new ();
7116 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7117 gtk_widget_show ( item );
7118 separator_created = TRUE;
7121 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7122 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7123 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7124 gtk_widget_show ( item );
7128 if ( wp && wp->image )
7130 if ( !separator_created ) {
7131 item = gtk_menu_item_new ();
7132 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7133 gtk_widget_show ( item );
7134 separator_created = TRUE;
7137 // Set up image paramater
7138 pass_along[5] = wp->image;
7140 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7141 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
7142 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7143 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7144 gtk_widget_show ( item );
7146 #ifdef VIK_CONFIG_GEOTAG
7147 GtkWidget *geotag_submenu = gtk_menu_new ();
7148 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7150 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7151 gtk_widget_show ( item );
7152 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7154 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7155 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7156 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7157 gtk_widget_show ( item );
7159 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7161 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7162 gtk_widget_show ( item );
7168 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7169 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7170 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7171 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7172 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7173 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7174 gtk_widget_show ( item );
7181 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7182 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7183 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7184 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7185 gtk_widget_show ( item );
7186 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7187 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7188 gtk_widget_set_sensitive ( item, TRUE );
7190 gtk_widget_set_sensitive ( item, FALSE );
7193 item = gtk_menu_item_new ();
7194 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7195 gtk_widget_show ( item );
7198 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7201 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7204 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7205 gtk_widget_show ( item );
7208 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7210 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7213 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7214 gtk_widget_show ( item );
7216 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7219 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7220 gtk_widget_show ( item );
7222 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7223 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7225 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7226 gtk_widget_show ( item );
7228 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7231 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7232 gtk_widget_show ( item );
7234 GtkWidget *vis_submenu = gtk_menu_new ();
7235 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7236 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7237 gtk_widget_show ( item );
7238 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7240 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7243 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7244 gtk_widget_show ( item );
7246 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7247 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7249 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7250 gtk_widget_show ( item );
7252 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7253 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7255 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7256 gtk_widget_show ( item );
7259 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7263 if ( l->current_track && !l->current_track->is_route ) {
7264 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7267 gtk_widget_show ( item );
7269 item = gtk_menu_item_new ();
7270 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7271 gtk_widget_show ( item );
7274 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7275 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7277 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7278 gtk_widget_show ( item );
7280 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7281 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7283 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7284 gtk_widget_show ( item );
7285 // Make it available only when a new track *not* already in progress
7286 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7288 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7289 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7292 gtk_widget_show ( item );
7294 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7295 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7297 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7298 gtk_widget_show ( item );
7300 GtkWidget *vis_submenu = gtk_menu_new ();
7301 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7302 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7303 gtk_widget_show ( item );
7304 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7306 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7307 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7308 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7309 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7310 gtk_widget_show ( item );
7312 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7313 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7315 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7316 gtk_widget_show ( item );
7318 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7319 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7321 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7322 gtk_widget_show ( item );
7324 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7325 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7326 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7327 gtk_widget_show ( item );
7330 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7334 if ( l->current_track && l->current_track->is_route ) {
7335 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7336 // Reuse finish track method
7337 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7338 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7339 gtk_widget_show ( item );
7341 item = gtk_menu_item_new ();
7342 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7343 gtk_widget_show ( item );
7346 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7347 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7348 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7349 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7350 gtk_widget_show ( item );
7352 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7353 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7355 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7356 gtk_widget_show ( item );
7357 // Make it available only when a new track *not* already in progress
7358 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7360 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7361 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7363 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7364 gtk_widget_show ( item );
7366 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7367 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7369 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7370 gtk_widget_show ( item );
7372 GtkWidget *vis_submenu = gtk_menu_new ();
7373 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7374 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7375 gtk_widget_show ( item );
7376 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7378 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7379 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7381 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7382 gtk_widget_show ( item );
7384 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7385 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7386 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7387 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7388 gtk_widget_show ( item );
7390 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7391 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7392 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7393 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7394 gtk_widget_show ( item );
7396 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7398 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7399 gtk_widget_show ( item );
7403 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7404 GtkWidget *submenu_sort = gtk_menu_new ();
7405 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7406 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7407 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7408 gtk_widget_show ( item );
7409 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7411 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7412 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7413 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7414 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7415 gtk_widget_show ( item );
7417 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7418 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7420 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7421 gtk_widget_show ( item );
7424 GtkWidget *upload_submenu = gtk_menu_new ();
7426 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7428 item = gtk_menu_item_new ();
7429 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7430 gtk_widget_show ( item );
7432 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7433 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7434 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7435 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7436 if ( l->current_track ) {
7437 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7438 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7439 gtk_widget_show ( item );
7442 item = gtk_menu_item_new ();
7443 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7444 gtk_widget_show ( item );
7447 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7448 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7450 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7451 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7452 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7453 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7454 gtk_widget_show ( item );
7456 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7457 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7458 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7459 gtk_widget_show ( item );
7461 GtkWidget *goto_submenu;
7462 goto_submenu = gtk_menu_new ();
7463 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7464 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7465 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7466 gtk_widget_show ( item );
7467 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7469 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7470 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7471 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7472 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7473 gtk_widget_show ( item );
7475 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7476 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7477 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7478 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7479 gtk_widget_show ( item );
7481 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7482 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7483 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7484 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7485 gtk_widget_show ( item );
7487 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7490 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7491 gtk_widget_show ( item );
7493 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7496 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7497 gtk_widget_show ( item );
7499 // Routes don't have speeds
7500 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7501 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7502 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7503 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7504 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7505 gtk_widget_show ( item );
7508 GtkWidget *combine_submenu;
7509 combine_submenu = gtk_menu_new ();
7510 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7512 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7513 gtk_widget_show ( item );
7514 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7516 // Routes don't have times or segments...
7517 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7518 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7519 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7520 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7521 gtk_widget_show ( item );
7523 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7524 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7525 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7526 gtk_widget_show ( item );
7529 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7530 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7531 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7532 gtk_widget_show ( item );
7534 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7535 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7537 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7538 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7539 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7540 gtk_widget_show ( item );
7542 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7543 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7545 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7546 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7547 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7548 gtk_widget_show ( item );
7550 GtkWidget *split_submenu;
7551 split_submenu = gtk_menu_new ();
7552 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7553 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7554 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7555 gtk_widget_show ( item );
7556 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7558 // Routes don't have times or segments...
7559 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7560 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7562 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7563 gtk_widget_show ( item );
7565 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7566 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7568 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7569 gtk_widget_show ( item );
7572 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7574 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7575 gtk_widget_show ( item );
7577 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7578 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7579 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7580 gtk_widget_show ( item );
7581 // Make it available only when a trackpoint is selected.
7582 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7584 GtkWidget *delete_submenu;
7585 delete_submenu = gtk_menu_new ();
7586 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7587 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7588 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7589 gtk_widget_show ( item );
7590 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7592 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7594 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7595 gtk_widget_show ( item );
7597 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7598 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7599 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7600 gtk_widget_show ( item );
7602 GtkWidget *transform_submenu;
7603 transform_submenu = gtk_menu_new ();
7604 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7605 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7606 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7607 gtk_widget_show ( item );
7608 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7610 GtkWidget *dem_submenu;
7611 dem_submenu = gtk_menu_new ();
7612 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7613 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
7614 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7615 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7617 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7619 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7620 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7621 gtk_widget_show ( item );
7623 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7624 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7625 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7626 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7627 gtk_widget_show ( item );
7629 GtkWidget *smooth_submenu;
7630 smooth_submenu = gtk_menu_new ();
7631 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7632 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7633 gtk_widget_show ( item );
7634 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7636 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7638 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7639 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7640 gtk_widget_show ( item );
7642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7643 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7644 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7645 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7646 gtk_widget_show ( item );
7648 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7649 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7651 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7652 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7653 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7654 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7655 gtk_widget_show ( item );
7657 // Routes don't have timestamps - so this is only available for tracks
7658 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7659 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7660 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7661 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7662 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7663 gtk_widget_show ( item );
7666 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7667 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7669 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7670 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7673 gtk_widget_show ( item );
7675 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7676 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7677 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7679 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7680 gtk_widget_show ( item );
7683 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7685 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7686 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7688 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7689 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
7690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7691 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7692 gtk_widget_show ( item );
7695 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7696 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7698 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7699 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7700 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7701 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7702 gtk_widget_show ( item );
7704 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7705 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7707 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7708 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7710 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7711 gtk_widget_show ( item );
7713 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7714 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7715 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
7716 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7717 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7718 gtk_widget_show ( item );
7721 // ATM can't upload a single waypoint but can do waypoints to a GPS
7722 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7724 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7725 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7726 gtk_widget_show ( item );
7727 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7729 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7730 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7731 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7732 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7733 gtk_widget_show ( item );
7737 #ifdef VIK_CONFIG_GOOGLE
7738 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7740 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7741 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7743 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7744 gtk_widget_show ( item );
7748 // Some things aren't usable with routes
7749 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7750 #ifdef VIK_CONFIG_OPENSTREETMAP
7751 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7752 // Convert internal pointer into actual track for usage outside this file
7753 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7754 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7756 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7757 gtk_widget_show ( item );
7760 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7761 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7763 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7764 gtk_widget_show ( item );
7766 /* ATM This function is only available via the layers panel, due to needing a vlp */
7768 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7769 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7770 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7772 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7773 gtk_widget_show ( item );
7777 #ifdef VIK_CONFIG_GEOTAG
7778 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7780 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7781 gtk_widget_show ( item );
7785 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7786 // Only show on viewport popmenu when a trackpoint is selected
7787 if ( ! vlp && l->current_tpl ) {
7789 item = gtk_menu_item_new ();
7790 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7791 gtk_widget_show ( item );
7793 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7794 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7795 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7796 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7797 gtk_widget_show ( item );
7801 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7802 GtkWidget *transform_submenu;
7803 transform_submenu = gtk_menu_new ();
7804 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7805 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7806 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7807 gtk_widget_show ( item );
7808 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7810 GtkWidget *dem_submenu;
7811 dem_submenu = gtk_menu_new ();
7812 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7813 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
7814 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7815 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7817 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7820 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7821 gtk_widget_show ( item );
7823 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
7825 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7826 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7827 gtk_widget_show ( item );
7830 gtk_widget_show_all ( GTK_WIDGET(menu) );
7835 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7838 if (!vtl->current_tpl)
7840 if (!vtl->current_tpl->next)
7843 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7844 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7846 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7849 VikTrackpoint *tp_new = vik_trackpoint_new();
7850 struct LatLon ll_current, ll_next;
7851 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7852 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7854 /* main positional interpolation */
7855 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7856 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7858 /* Now other properties that can be interpolated */
7859 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7861 if (tp_current->has_timestamp && tp_next->has_timestamp) {
7862 /* Note here the division is applied to each part, then added
7863 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7864 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7865 tp_new->has_timestamp = TRUE;
7868 if (tp_current->speed != NAN && tp_next->speed != NAN)
7869 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7871 /* TODO - improve interpolation of course, as it may not be correct.
7872 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7873 [similar applies if value is in radians] */
7874 if (tp_current->course != NAN && tp_next->course != NAN)
7875 tp_new->course = (tp_current->course + tp_next->course)/2;
7877 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7879 /* Insert new point into the trackpoints list after the current TP */
7880 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7882 // Otherwise try routes
7883 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7887 gint index = g_list_index ( trk->trackpoints, tp_current );
7889 // NB no recalculation of bounds since it is inserted between points
7890 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7895 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7901 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7905 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7907 if ( vtl->current_tpl )
7909 vtl->current_tpl = NULL;
7910 vtl->current_tp_track = NULL;
7911 vtl->current_tp_id = NULL;
7912 vik_layer_emit_update(VIK_LAYER(vtl));
7916 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7918 g_assert ( vtl->tpwin != NULL );
7919 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7920 trw_layer_cancel_current_tp ( vtl, TRUE );
7922 if ( vtl->current_tpl == NULL )
7925 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7927 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7928 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7930 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7932 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7934 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7940 // Find available adjacent trackpoint
7941 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7943 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7944 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7946 // Delete current trackpoint
7947 vik_trackpoint_free ( vtl->current_tpl->data );
7948 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7950 // Set to current to the available adjacent trackpoint
7951 vtl->current_tpl = new_tpl;
7953 // Reset dialog with the available adjacent trackpoint
7954 if ( vtl->current_tp_track ) {
7955 vik_track_calculate_bounds ( vtl->current_tp_track );
7956 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7959 vik_layer_emit_update(VIK_LAYER(vtl));
7963 // Delete current trackpoint
7964 vik_trackpoint_free ( vtl->current_tpl->data );
7965 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7966 trw_layer_cancel_current_tp ( vtl, FALSE );
7969 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7971 if ( vtl->current_tp_track )
7972 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7973 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7975 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7977 if ( vtl->current_tp_track )
7978 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7979 vik_layer_emit_update(VIK_LAYER(vtl));
7981 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7983 trw_layer_insert_tp_after_current_tp ( vtl );
7984 vik_layer_emit_update(VIK_LAYER(vtl));
7986 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7987 vik_layer_emit_update(VIK_LAYER(vtl));
7991 * trw_layer_dialog_shift:
7992 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7994 * Try to reposition a dialog if it's over the specified coord
7995 * so to not obscure the item of interest
7997 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7999 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8001 // Attempt force dialog to be shown so we can find out where it is more reliably...
8002 while ( gtk_events_pending() )
8003 gtk_main_iteration ();
8005 // get parent window position & size
8006 gint win_pos_x, win_pos_y;
8007 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8009 gint win_size_x, win_size_y;
8010 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8012 // get own dialog size
8013 gint dia_size_x, dia_size_y;
8014 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8016 // get own dialog position
8017 gint dia_pos_x, dia_pos_y;
8018 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8020 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8021 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8023 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8025 gint vp_xx, vp_yy; // In viewport pixels
8026 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8028 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8032 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8034 // Transform Viewport pixels into absolute pixels
8035 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8036 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8038 // Is dialog over the point (to within an ^^ edge value)
8039 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8040 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8044 gint hh = vik_viewport_get_height ( vvp );
8046 // Consider the difference in viewport to the full window
8047 gint offset_y = dest_y;
8048 // Add difference between dialog and window sizes
8049 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8051 if ( vp_yy > hh/2 ) {
8052 // Point in bottom half, move window to top half
8053 gtk_window_move ( dialog, dia_pos_x, offset_y );
8056 // Point in top half, move dialog down
8057 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8061 // Shift left<->right
8062 gint ww = vik_viewport_get_width ( vvp );
8064 // Consider the difference in viewport to the full window
8065 gint offset_x = dest_x;
8066 // Add difference between dialog and window sizes
8067 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8069 if ( vp_xx > ww/2 ) {
8070 // Point on right, move window to left
8071 gtk_window_move ( dialog, offset_x, dia_pos_y );
8074 // Point on left, move right
8075 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8083 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8087 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8088 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8089 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8090 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8092 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8094 if ( vtl->current_tpl ) {
8095 // get tp pixel position
8096 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8098 // Shift up<->down to try not to obscure the trackpoint.
8099 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8103 if ( vtl->current_tpl )
8104 if ( vtl->current_tp_track )
8105 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8106 /* set layer name and TP data */
8109 /***************************************************************************
8111 ***************************************************************************/
8113 /*** Utility data structures and functions ****/
8117 gint closest_x, closest_y;
8118 gboolean draw_images;
8119 gpointer *closest_wp_id;
8120 VikWaypoint *closest_wp;
8126 gint closest_x, closest_y;
8127 gpointer closest_track_id;
8128 VikTrackpoint *closest_tp;
8134 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8140 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8142 // If waypoint has an image then use the image size to select
8143 if ( params->draw_images && wp->image ) {
8144 gint slackx, slacky;
8145 slackx = wp->image_width / 2;
8146 slacky = wp->image_height / 2;
8148 if ( x <= params->x + slackx && x >= params->x - slackx
8149 && y <= params->y + slacky && y >= params->y - slacky ) {
8150 params->closest_wp_id = id;
8151 params->closest_wp = wp;
8152 params->closest_x = x;
8153 params->closest_y = y;
8156 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8157 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8158 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8160 params->closest_wp_id = id;
8161 params->closest_wp = wp;
8162 params->closest_x = x;
8163 params->closest_y = y;
8167 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8169 GList *tpl = t->trackpoints;
8175 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8181 tp = VIK_TRACKPOINT(tpl->data);
8183 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8185 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8186 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8187 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8189 params->closest_track_id = id;
8190 params->closest_tp = tp;
8191 params->closest_tpl = tpl;
8192 params->closest_x = x;
8193 params->closest_y = y;
8199 // ATM: Leave this as 'Track' only.
8200 // Not overly bothered about having a snap to route trackpoint capability
8201 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8203 TPSearchParams params;
8207 params.closest_track_id = NULL;
8208 params.closest_tp = NULL;
8209 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8210 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8211 return params.closest_tp;
8214 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8216 WPSearchParams params;
8220 params.draw_images = vtl->drawimages;
8221 params.closest_wp = NULL;
8222 params.closest_wp_id = NULL;
8223 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8224 return params.closest_wp;
8228 // Some forward declarations
8229 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8230 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8231 static void marker_end_move ( tool_ed_t *t );
8234 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8238 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8240 // Here always allow snapping back to the original location
8241 // this is useful when one decides not to move the thing afterall
8242 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8245 if ( event->state & GDK_CONTROL_MASK )
8247 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8249 new_coord = tp->coord;
8253 if ( event->state & GDK_SHIFT_MASK )
8255 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8257 new_coord = wp->coord;
8261 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8263 marker_moveto ( t, x, y );
8270 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8272 if ( t->holding && event->button == 1 )
8275 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8278 if ( event->state & GDK_CONTROL_MASK )
8280 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8282 new_coord = tp->coord;
8286 if ( event->state & GDK_SHIFT_MASK )
8288 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8290 new_coord = wp->coord;
8293 marker_end_move ( t );
8295 // Determine if working on a waypoint or a trackpoint
8296 if ( t->is_waypoint ) {
8297 // Update waypoint position
8298 vtl->current_wp->coord = new_coord;
8299 trw_layer_calculate_bounds_waypoints ( vtl );
8300 // Reset waypoint pointer
8301 vtl->current_wp = NULL;
8302 vtl->current_wp_id = NULL;
8305 if ( vtl->current_tpl ) {
8306 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8308 if ( vtl->current_tp_track )
8309 vik_track_calculate_bounds ( vtl->current_tp_track );
8312 if ( vtl->current_tp_track )
8313 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8314 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8318 vik_layer_emit_update ( VIK_LAYER(vtl) );
8325 Returns true if a waypoint or track is found near the requested event position for this particular layer
8326 The item found is automatically selected
8327 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8329 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8331 if ( event->button != 1 )
8334 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8337 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8341 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8343 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8345 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8346 WPSearchParams wp_params;
8347 wp_params.vvp = vvp;
8348 wp_params.x = event->x;
8349 wp_params.y = event->y;
8350 wp_params.draw_images = vtl->drawimages;
8351 wp_params.closest_wp_id = NULL;
8352 wp_params.closest_wp = NULL;
8354 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8356 if ( wp_params.closest_wp ) {
8359 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8361 // Too easy to move it so must be holding shift to start immediately moving it
8362 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8363 if ( event->state & GDK_SHIFT_MASK ||
8364 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8365 // Put into 'move buffer'
8366 // NB vvp & vw already set in tet
8367 tet->vtl = (gpointer)vtl;
8368 tet->is_waypoint = TRUE;
8370 marker_begin_move (tet, event->x, event->y);
8373 vtl->current_wp = wp_params.closest_wp;
8374 vtl->current_wp_id = wp_params.closest_wp_id;
8376 vik_layer_emit_update ( VIK_LAYER(vtl) );
8382 // Used for both track and route lists
8383 TPSearchParams tp_params;
8384 tp_params.vvp = vvp;
8385 tp_params.x = event->x;
8386 tp_params.y = event->y;
8387 tp_params.closest_track_id = NULL;
8388 tp_params.closest_tp = NULL;
8389 tp_params.closest_tpl = NULL;
8390 tp_params.bbox = bbox;
8392 if (vtl->tracks_visible) {
8393 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8395 if ( tp_params.closest_tp ) {
8397 // Always select + highlight the track
8398 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8400 tet->is_waypoint = FALSE;
8402 // Select the Trackpoint
8403 // Can move it immediately when control held or it's the previously selected tp
8404 if ( event->state & GDK_CONTROL_MASK ||
8405 vtl->current_tpl == tp_params.closest_tpl ) {
8406 // Put into 'move buffer'
8407 // NB vvp & vw already set in tet
8408 tet->vtl = (gpointer)vtl;
8409 marker_begin_move (tet, event->x, event->y);
8412 vtl->current_tpl = tp_params.closest_tpl;
8413 vtl->current_tp_id = tp_params.closest_track_id;
8414 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8416 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8419 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8421 vik_layer_emit_update ( VIK_LAYER(vtl) );
8426 // Try again for routes
8427 if (vtl->routes_visible) {
8428 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8430 if ( tp_params.closest_tp ) {
8432 // Always select + highlight the track
8433 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8435 tet->is_waypoint = FALSE;
8437 // Select the Trackpoint
8438 // Can move it immediately when control held or it's the previously selected tp
8439 if ( event->state & GDK_CONTROL_MASK ||
8440 vtl->current_tpl == tp_params.closest_tpl ) {
8441 // Put into 'move buffer'
8442 // NB vvp & vw already set in tet
8443 tet->vtl = (gpointer)vtl;
8444 marker_begin_move (tet, event->x, event->y);
8447 vtl->current_tpl = tp_params.closest_tpl;
8448 vtl->current_tp_id = tp_params.closest_track_id;
8449 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8451 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8454 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8456 vik_layer_emit_update ( VIK_LAYER(vtl) );
8461 /* these aren't the droids you're looking for */
8462 vtl->current_wp = NULL;
8463 vtl->current_wp_id = NULL;
8464 trw_layer_cancel_current_tp ( vtl, FALSE );
8467 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8472 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8474 if ( event->button != 3 )
8477 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8480 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8483 /* Post menu for the currently selected item */
8485 /* See if a track is selected */
8486 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8487 if ( track && track->visible ) {
8489 if ( track->name ) {
8491 if ( vtl->track_right_click_menu )
8492 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8494 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8501 if ( track->is_route )
8502 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8504 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8506 if ( trkf && udataU.uuid ) {
8509 if ( track->is_route )
8510 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8512 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8514 trw_layer_sublayer_add_menu_items ( vtl,
8515 vtl->track_right_click_menu,
8517 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8523 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8529 /* See if a waypoint is selected */
8530 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8531 if ( waypoint && waypoint->visible ) {
8532 if ( waypoint->name ) {
8534 if ( vtl->wp_right_click_menu )
8535 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8537 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8540 udata.wp = waypoint;
8543 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8545 if ( wpf && udata.uuid ) {
8546 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8548 trw_layer_sublayer_add_menu_items ( vtl,
8549 vtl->wp_right_click_menu,
8551 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8556 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8565 /* background drawing hook, to be passed the viewport */
8566 static gboolean tool_sync_done = TRUE;
8568 static gboolean tool_sync(gpointer data)
8570 VikViewport *vvp = data;
8571 gdk_threads_enter();
8572 vik_viewport_sync(vvp);
8573 tool_sync_done = TRUE;
8574 gdk_threads_leave();
8578 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8581 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8582 gdk_gc_set_function ( t->gc, GDK_INVERT );
8583 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8584 vik_viewport_sync(t->vvp);
8589 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8591 VikViewport *vvp = t->vvp;
8592 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8593 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8597 if (tool_sync_done) {
8598 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8599 tool_sync_done = FALSE;
8603 static void marker_end_move ( tool_ed_t *t )
8605 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8606 g_object_unref ( t->gc );
8610 /*** Edit waypoint ****/
8612 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8614 tool_ed_t *t = g_new(tool_ed_t, 1);
8620 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8625 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8627 WPSearchParams params;
8628 tool_ed_t *t = data;
8629 VikViewport *vvp = t->vvp;
8631 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8638 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8641 if ( vtl->current_wp && vtl->current_wp->visible )
8643 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8645 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8647 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8648 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8650 if ( event->button == 3 )
8651 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8653 marker_begin_move(t, event->x, event->y);
8660 params.x = event->x;
8661 params.y = event->y;
8662 params.draw_images = vtl->drawimages;
8663 params.closest_wp_id = NULL;
8664 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8665 params.closest_wp = NULL;
8666 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8667 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8669 // how do we get here?
8670 marker_begin_move(t, event->x, event->y);
8671 g_critical("shouldn't be here");
8674 else if ( params.closest_wp )
8676 if ( event->button == 3 )
8677 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8679 vtl->waypoint_rightclick = FALSE;
8681 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8683 vtl->current_wp = params.closest_wp;
8684 vtl->current_wp_id = params.closest_wp_id;
8686 /* could make it so don't update if old WP is off screen and new is null but oh well */
8687 vik_layer_emit_update ( VIK_LAYER(vtl) );
8691 vtl->current_wp = NULL;
8692 vtl->current_wp_id = NULL;
8693 vtl->waypoint_rightclick = FALSE;
8694 vik_layer_emit_update ( VIK_LAYER(vtl) );
8698 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8700 tool_ed_t *t = data;
8701 VikViewport *vvp = t->vvp;
8703 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8708 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8711 if ( event->state & GDK_CONTROL_MASK )
8713 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8715 new_coord = tp->coord;
8719 if ( event->state & GDK_SHIFT_MASK )
8721 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8722 if ( wp && wp != vtl->current_wp )
8723 new_coord = wp->coord;
8728 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8730 marker_moveto ( t, x, y );
8737 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8739 tool_ed_t *t = data;
8740 VikViewport *vvp = t->vvp;
8742 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8745 if ( t->holding && event->button == 1 )
8748 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8751 if ( event->state & GDK_CONTROL_MASK )
8753 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8755 new_coord = tp->coord;
8759 if ( event->state & GDK_SHIFT_MASK )
8761 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8762 if ( wp && wp != vtl->current_wp )
8763 new_coord = wp->coord;
8766 marker_end_move ( t );
8768 vtl->current_wp->coord = new_coord;
8770 trw_layer_calculate_bounds_waypoints ( vtl );
8771 vik_layer_emit_update ( VIK_LAYER(vtl) );
8774 /* PUT IN RIGHT PLACE!!! */
8775 if ( event->button == 3 && vtl->waypoint_rightclick )
8777 if ( vtl->wp_right_click_menu )
8778 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8779 if ( vtl->current_wp ) {
8780 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8781 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 );
8782 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8784 vtl->waypoint_rightclick = FALSE;
8789 /*** New track ****/
8791 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8798 GdkDrawable *drawable;
8804 * Draw specified pixmap
8806 static gboolean draw_sync ( gpointer data )
8808 draw_sync_t *ds = (draw_sync_t*) data;
8809 // Sometimes don't want to draw
8810 // normally because another update has taken precedent such as panning the display
8811 // which means this pixmap is no longer valid
8812 if ( ds->vtl->draw_sync_do ) {
8813 gdk_threads_enter();
8814 gdk_draw_drawable (ds->drawable,
8817 0, 0, 0, 0, -1, -1);
8818 ds->vtl->draw_sync_done = TRUE;
8819 gdk_threads_leave();
8825 static gchar* distance_string (gdouble distance)
8829 /* draw label with distance */
8830 vik_units_distance_t dist_units = a_vik_get_units_distance ();
8831 switch (dist_units) {
8832 case VIK_UNITS_DISTANCE_MILES:
8833 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8834 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8835 } else if (distance < 1609.4) {
8836 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8838 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8842 // VIK_UNITS_DISTANCE_KILOMETRES
8843 if (distance >= 1000 && distance < 100000) {
8844 g_sprintf(str, "%3.2f km", distance/1000.0);
8845 } else if (distance < 1000) {
8846 g_sprintf(str, "%d m", (int)distance);
8848 g_sprintf(str, "%d km", (int)distance/1000);
8852 return g_strdup (str);
8856 * Actually set the message in statusbar
8858 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8860 // Only show elevation data when track has some elevation properties
8861 gchar str_gain_loss[64];
8862 str_gain_loss[0] = '\0';
8863 gchar str_last_step[64];
8864 str_last_step[0] = '\0';
8865 gchar *str_total = distance_string (distance);
8867 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8868 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8869 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8871 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8874 if ( last_step > 0 ) {
8875 gchar *tmp = distance_string (last_step);
8876 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8880 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8882 // Write with full gain/loss information
8883 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8884 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8886 g_free ( str_total );
8890 * Figure out what information should be set in the statusbar and then write it
8892 static void update_statusbar ( VikTrwLayer *vtl )
8894 // Get elevation data
8895 gdouble elev_gain, elev_loss;
8896 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8898 /* Find out actual distance of current track */
8899 gdouble distance = vik_track_get_length (vtl->current_track);
8901 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8905 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8907 /* if we haven't sync'ed yet, we don't have time to do more. */
8908 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8909 GList *iter = g_list_last ( vtl->current_track->trackpoints );
8910 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8912 static GdkPixmap *pixmap = NULL;
8914 // Need to check in case window has been resized
8915 w1 = vik_viewport_get_width(vvp);
8916 h1 = vik_viewport_get_height(vvp);
8918 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8920 gdk_drawable_get_size (pixmap, &w2, &h2);
8921 if (w1 != w2 || h1 != h2) {
8922 g_object_unref ( G_OBJECT ( pixmap ) );
8923 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8926 // Reset to background
8927 gdk_draw_drawable (pixmap,
8928 vtl->current_track_newpoint_gc,
8929 vik_viewport_get_pixmap(vvp),
8930 0, 0, 0, 0, -1, -1);
8932 draw_sync_t *passalong;
8935 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8937 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8938 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8939 // thus when we come to reset to the background it would include what we have already drawn!!
8940 gdk_draw_line ( pixmap,
8941 vtl->current_track_newpoint_gc,
8942 x1, y1, event->x, event->y );
8943 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8945 /* Find out actual distance of current track */
8946 gdouble distance = vik_track_get_length (vtl->current_track);
8948 // Now add distance to where the pointer is //
8951 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8952 vik_coord_to_latlon ( &coord, &ll );
8953 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8954 distance = distance + last_step;
8956 // Get elevation data
8957 gdouble elev_gain, elev_loss;
8958 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8960 // Adjust elevation data (if available) for the current pointer position
8962 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8963 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8964 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8965 // Adjust elevation of last track point
8966 if ( elev_new > last_tpt->altitude )
8968 elev_gain += elev_new - last_tpt->altitude;
8971 elev_loss += last_tpt->altitude - elev_new;
8976 // Display of the distance 'tooltip' during track creation is controlled by a preference
8978 if ( a_vik_get_create_track_tooltip() ) {
8980 gchar *str = distance_string (distance);
8982 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8983 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8984 pango_layout_set_text (pl, str, -1);
8986 pango_layout_get_pixel_size ( pl, &wd, &hd );
8989 // offset from cursor a bit depending on font size
8993 // Create a background block to make the text easier to read over the background map
8994 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8995 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8996 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8998 g_object_unref ( G_OBJECT ( pl ) );
8999 g_object_unref ( G_OBJECT ( background_block_gc ) );
9003 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9004 passalong->vtl = vtl;
9005 passalong->pixmap = pixmap;
9006 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9007 passalong->gc = vtl->current_track_newpoint_gc;
9011 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9013 // Update statusbar with full gain/loss information
9014 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9016 // draw pixmap when we have time to
9017 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9018 vtl->draw_sync_done = FALSE;
9019 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9021 return VIK_LAYER_TOOL_ACK;
9024 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9026 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9027 vtl->current_track = NULL;
9028 vik_layer_emit_update ( VIK_LAYER(vtl) );
9030 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9032 if ( vtl->current_track->trackpoints )
9034 GList *last = g_list_last(vtl->current_track->trackpoints);
9035 g_free ( last->data );
9036 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9039 update_statusbar ( vtl );
9041 vik_layer_emit_update ( VIK_LAYER(vtl) );
9048 * Common function to handle trackpoint button requests on either a route or a track
9049 * . enables adding a point via normal click
9050 * . enables removal of last point via right click
9051 * . finishing of the track or route via double clicking
9053 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9057 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9060 if ( event->button == 2 ) {
9061 // As the display is panning, the new track pixmap is now invalid so don't draw it
9062 // otherwise this drawing done results in flickering back to an old image
9063 vtl->draw_sync_do = FALSE;
9067 if ( event->button == 3 )
9069 if ( !vtl->current_track )
9072 if ( vtl->current_track->trackpoints )
9074 GList *last = g_list_last(vtl->current_track->trackpoints);
9075 g_free ( last->data );
9076 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9078 vik_track_calculate_bounds ( vtl->current_track );
9079 update_statusbar ( vtl );
9081 vik_layer_emit_update ( VIK_LAYER(vtl) );
9085 if ( event->type == GDK_2BUTTON_PRESS )
9087 /* subtract last (duplicate from double click) tp then end */
9088 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9090 GList *last = g_list_last(vtl->current_track->trackpoints);
9091 g_free ( last->data );
9092 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9093 /* undo last, then end */
9094 vtl->current_track = NULL;
9096 vik_layer_emit_update ( VIK_LAYER(vtl) );
9100 tp = vik_trackpoint_new();
9101 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9103 /* snap to other TP */
9104 if ( event->state & GDK_CONTROL_MASK )
9106 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9108 tp->coord = other_tp->coord;
9111 tp->newsegment = FALSE;
9112 tp->has_timestamp = FALSE;
9115 if ( vtl->current_track ) {
9116 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9117 /* Auto attempt to get elevation from DEM data (if it's available) */
9118 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9121 vtl->ct_x1 = vtl->ct_x2;
9122 vtl->ct_y1 = vtl->ct_y2;
9123 vtl->ct_x2 = event->x;
9124 vtl->ct_y2 = event->y;
9126 vik_layer_emit_update ( VIK_LAYER(vtl) );
9130 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9132 // ----------------------------------------------------- if current is a route - switch to new track
9133 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9135 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9136 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9138 new_track_create_common ( vtl, name );
9144 return tool_new_track_or_route_click ( vtl, event, vvp );
9147 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9149 if ( event->button == 2 ) {
9150 // Pan moving ended - enable potential point drawing again
9151 vtl->draw_sync_do = TRUE;
9152 vtl->draw_sync_done = TRUE;
9156 /*** New route ****/
9158 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9163 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9165 // -------------------------- if current is a track - switch to new route
9166 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9168 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9169 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9170 new_route_create_common ( vtl, name );
9176 return tool_new_track_or_route_click ( vtl, event, vvp );
9179 /*** New waypoint ****/
9181 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9186 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9189 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9191 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9192 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9193 trw_layer_calculate_bounds_waypoints ( vtl );
9194 vik_layer_emit_update ( VIK_LAYER(vtl) );
9200 /*** Edit trackpoint ****/
9202 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9204 tool_ed_t *t = g_new(tool_ed_t, 1);
9210 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9215 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9217 tool_ed_t *t = data;
9218 VikViewport *vvp = t->vvp;
9219 TPSearchParams params;
9220 /* OUTDATED DOCUMENTATION:
9221 find 5 pixel range on each side. then put these UTM, and a pointer
9222 to the winning track name (and maybe the winning track itself), and a
9223 pointer to the winning trackpoint, inside an array or struct. pass
9224 this along, do a foreach on the tracks which will do a foreach on the
9227 params.x = event->x;
9228 params.y = event->y;
9229 params.closest_track_id = NULL;
9230 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9231 params.closest_tp = NULL;
9232 params.closest_tpl = NULL;
9233 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9235 if ( event->button != 1 )
9238 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9241 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9244 if ( vtl->current_tpl )
9246 /* 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.) */
9247 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9248 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9253 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9255 if ( current_tr->visible &&
9256 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9257 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9258 marker_begin_move ( t, event->x, event->y );
9264 if ( vtl->tracks_visible )
9265 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9267 if ( params.closest_tp )
9269 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9270 vtl->current_tpl = params.closest_tpl;
9271 vtl->current_tp_id = params.closest_track_id;
9272 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9273 trw_layer_tpwin_init ( vtl );
9274 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9275 vik_layer_emit_update ( VIK_LAYER(vtl) );
9279 if ( vtl->routes_visible )
9280 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9282 if ( params.closest_tp )
9284 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9285 vtl->current_tpl = params.closest_tpl;
9286 vtl->current_tp_id = params.closest_track_id;
9287 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9288 trw_layer_tpwin_init ( vtl );
9289 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9290 vik_layer_emit_update ( VIK_LAYER(vtl) );
9294 /* these aren't the droids you're looking for */
9298 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9300 tool_ed_t *t = data;
9301 VikViewport *vvp = t->vvp;
9303 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9309 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9312 if ( event->state & GDK_CONTROL_MASK )
9314 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9315 if ( tp && tp != vtl->current_tpl->data )
9316 new_coord = tp->coord;
9318 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9321 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9322 marker_moveto ( t, x, y );
9330 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9332 tool_ed_t *t = data;
9333 VikViewport *vvp = t->vvp;
9335 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9337 if ( event->button != 1)
9342 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9345 if ( event->state & GDK_CONTROL_MASK )
9347 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9348 if ( tp && tp != vtl->current_tpl->data )
9349 new_coord = tp->coord;
9352 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9353 if ( vtl->current_tp_track )
9354 vik_track_calculate_bounds ( vtl->current_tp_track );
9356 marker_end_move ( t );
9358 /* diff dist is diff from orig */
9360 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9362 vik_layer_emit_update ( VIK_LAYER(vtl) );
9369 /*** Route Finder ***/
9370 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9375 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9378 if ( !vtl ) return FALSE;
9379 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9380 if ( event->button == 3 && vtl->route_finder_current_track ) {
9382 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9384 vtl->route_finder_coord = *new_end;
9386 vik_layer_emit_update ( VIK_LAYER(vtl) );
9387 /* remove last ' to:...' */
9388 if ( vtl->route_finder_current_track->comment ) {
9389 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9390 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9391 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9392 last_to - vtl->route_finder_current_track->comment - 1);
9393 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9398 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9399 struct LatLon start, end;
9401 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9402 vik_coord_to_latlon ( &(tmp), &end );
9403 vtl->route_finder_coord = tmp; /* for continuations */
9405 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9406 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9407 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9409 vtl->route_finder_check_added_track = TRUE;
9410 vtl->route_finder_started = FALSE;
9413 vik_routing_default_find ( vtl, start, end);
9415 /* see if anything was done -- a track was added or appended to */
9416 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9417 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 ) );
9418 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9419 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9420 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9421 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9424 if ( vtl->route_finder_added_track )
9425 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9427 vtl->route_finder_added_track = NULL;
9428 vtl->route_finder_check_added_track = FALSE;
9429 vtl->route_finder_append = FALSE;
9431 vik_layer_emit_update ( VIK_LAYER(vtl) );
9433 vtl->route_finder_started = TRUE;
9434 vtl->route_finder_coord = tmp;
9435 vtl->route_finder_current_track = NULL;
9440 /*** Show picture ****/
9442 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9447 /* Params are: vvp, event, last match found or NULL */
9448 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9450 if ( wp->image && wp->visible )
9452 gint x, y, slackx, slacky;
9453 GdkEventButton *event = (GdkEventButton *) params[1];
9455 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9456 slackx = wp->image_width / 2;
9457 slacky = wp->image_height / 2;
9458 if ( x <= event->x + slackx && x >= event->x - slackx
9459 && y <= event->y + slacky && y >= event->y - slacky )
9461 params[2] = wp->image; /* we've found a match. however continue searching
9462 * since we want to find the last match -- that
9463 * is, the match that was drawn last. */
9468 static void trw_layer_show_picture ( gpointer pass_along[6] )
9470 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9472 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9475 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9476 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9477 g_free ( quoted_file );
9478 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9480 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() );
9481 g_error_free ( err );
9484 #endif /* WINDOWS */
9487 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9489 gpointer params[3] = { vvp, event, NULL };
9490 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9492 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9495 static gpointer pass_along[6];
9496 pass_along[0] = vtl;
9497 pass_along[5] = params[2];
9498 trw_layer_show_picture ( pass_along );
9499 return TRUE; /* found a match */
9502 return FALSE; /* go through other layers, searching for a match */
9505 /***************************************************************************
9507 ***************************************************************************/
9510 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9512 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9513 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9516 /* Structure for thumbnail creating data used in the background thread */
9518 VikTrwLayer *vtl; // Layer needed for redrawing
9519 GSList *pics; // Image list
9520 } thumbnail_create_thread_data;
9522 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9524 guint total = g_slist_length(tctd->pics), done = 0;
9525 while ( tctd->pics )
9527 a_thumbnails_create ( (gchar *) tctd->pics->data );
9528 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9530 return -1; /* Abort thread */
9532 tctd->pics = tctd->pics->next;
9535 // Redraw to show the thumbnails as they are now created
9536 if ( IS_VIK_LAYER(tctd->vtl) )
9537 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9542 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9544 while ( tctd->pics )
9546 g_free ( tctd->pics->data );
9547 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9552 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9554 if ( ! vtl->has_verified_thumbnails )
9556 GSList *pics = NULL;
9557 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9560 gint len = g_slist_length ( pics );
9561 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9562 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9565 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9567 (vik_thr_func) create_thumbnails_thread,
9569 (vik_thr_free_func) thumbnail_create_thread_free,
9577 static const gchar* my_track_colors ( gint ii )
9579 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9591 // Fast and reliable way of returning a colour
9592 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9595 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9597 GHashTableIter iter;
9598 gpointer key, value;
9602 g_hash_table_iter_init ( &iter, vtl->tracks );
9604 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9606 // Tracks get a random spread of colours if not already assigned
9607 if ( ! VIK_TRACK(value)->has_color ) {
9608 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9609 VIK_TRACK(value)->color = vtl->track_color;
9611 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9613 VIK_TRACK(value)->has_color = TRUE;
9616 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9619 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9625 g_hash_table_iter_init ( &iter, vtl->routes );
9627 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9629 // Routes get an intermix of reds
9630 if ( ! VIK_TRACK(value)->has_color ) {
9632 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9634 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9635 VIK_TRACK(value)->has_color = TRUE;
9638 trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
9645 * (Re)Calculate the bounds of the waypoints in this layer,
9646 * This should be called whenever waypoints are changed
9648 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9650 struct LatLon topleft = { 0.0, 0.0 };
9651 struct LatLon bottomright = { 0.0, 0.0 };
9654 GHashTableIter iter;
9655 gpointer key, value;
9657 g_hash_table_iter_init ( &iter, vtl->waypoints );
9659 // Set bounds to first point
9660 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9661 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9662 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9665 // Ensure there is another point...
9666 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9668 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9670 // See if this point increases the bounds.
9671 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9673 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9674 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9675 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9676 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9680 vtl->waypoints_bbox.north = topleft.lat;
9681 vtl->waypoints_bbox.east = bottomright.lon;
9682 vtl->waypoints_bbox.south = bottomright.lat;
9683 vtl->waypoints_bbox.west = topleft.lon;
9686 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9688 vik_track_calculate_bounds ( trk );
9691 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9693 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9694 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9697 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9699 if ( ! VIK_LAYER(vtl)->vt )
9702 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9703 if ( g_hash_table_size (vtl->tracks) > 1 )
9704 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9706 if ( g_hash_table_size (vtl->routes) > 1 )
9707 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9709 if ( g_hash_table_size (vtl->waypoints) > 1 )
9710 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9713 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9715 if ( VIK_LAYER(vtl)->realized )
9716 trw_layer_verify_thumbnails ( vtl, vvp );
9717 trw_layer_track_alloc_colors ( vtl );
9719 trw_layer_calculate_bounds_waypoints ( vtl );
9720 trw_layer_calculate_bounds_tracks ( vtl );
9722 // Apply treeview sort after loading all the tracks for this layer
9723 // (rather than sorted insert on each individual track additional)
9724 // and after subsequent changes to the properties as the specified order may have changed.
9725 // since the sorting of a treeview section is now very quick
9726 // NB sorting is also performed after every name change as well to maintain the list order
9727 trw_layer_sort_all ( vtl );
9730 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9732 return vtl->coord_mode;
9736 * Uniquify the whole layer
9737 * Also requires the layers panel as the names shown there need updating too
9738 * Returns whether the operation was successful or not
9740 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9743 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9744 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9745 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9751 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9753 vik_coord_convert ( &(wp->coord), *dest_mode );
9756 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9758 vik_track_convert ( tr, *dest_mode );
9761 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9763 if ( vtl->coord_mode != dest_mode )
9765 vtl->coord_mode = dest_mode;
9766 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9767 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9768 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9772 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9774 vtl->menu_selection = selection;
9777 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9779 return (vtl->menu_selection);
9782 /* ----------- Downloading maps along tracks --------------- */
9784 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9786 /* TODO: calculating based on current size of viewport */
9787 const gdouble w_at_zoom_0_125 = 0.0013;
9788 const gdouble h_at_zoom_0_125 = 0.0011;
9789 gdouble zoom_factor = zoom_level/0.125;
9791 wh->lat = h_at_zoom_0_125 * zoom_factor;
9792 wh->lon = w_at_zoom_0_125 * zoom_factor;
9794 return 0; /* all OK */
9797 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9799 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9800 (dist->lat >= ABS(to->north_south - from->north_south)))
9803 VikCoord *coord = g_malloc(sizeof(VikCoord));
9804 coord->mode = VIK_COORD_LATLON;
9806 if (ABS(gradient) < 1) {
9807 if (from->east_west > to->east_west)
9808 coord->east_west = from->east_west - dist->lon;
9810 coord->east_west = from->east_west + dist->lon;
9811 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9813 if (from->north_south > to->north_south)
9814 coord->north_south = from->north_south - dist->lat;
9816 coord->north_south = from->north_south + dist->lat;
9817 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9823 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9825 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9826 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9828 VikCoord *next = from;
9830 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9832 list = g_list_prepend(list, next);
9838 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9840 typedef struct _Rect {
9845 #define GLRECT(iter) ((Rect *)((iter)->data))
9848 GList *rects_to_download = NULL;
9851 if (get_download_area_width(vvp, zoom_level, &wh))
9854 GList *iter = tr->trackpoints;
9858 gboolean new_map = TRUE;
9859 VikCoord *cur_coord, tl, br;
9862 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9864 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9865 rect = g_malloc(sizeof(Rect));
9868 rect->center = *cur_coord;
9869 rects_to_download = g_list_prepend(rects_to_download, rect);
9874 gboolean found = FALSE;
9875 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9876 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9887 GList *fillins = NULL;
9888 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9889 /* seems that ATM the function get_next_coord works only for LATLON */
9890 if ( cur_coord->mode == VIK_COORD_LATLON ) {
9891 /* fill-ins for far apart points */
9892 GList *cur_rect, *next_rect;
9893 for (cur_rect = rects_to_download;
9894 (next_rect = cur_rect->next) != NULL;
9895 cur_rect = cur_rect->next) {
9896 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9897 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9898 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9902 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9905 GList *iter = fillins;
9907 cur_coord = (VikCoord *)(iter->data);
9908 vik_coord_set_area(cur_coord, &wh, &tl, &br);
9909 rect = g_malloc(sizeof(Rect));
9912 rect->center = *cur_coord;
9913 rects_to_download = g_list_prepend(rects_to_download, rect);
9918 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9919 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9923 for (iter = fillins; iter; iter = iter->next)
9925 g_list_free(fillins);
9927 if (rects_to_download) {
9928 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9929 g_free(rect_iter->data);
9930 g_list_free(rects_to_download);
9934 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9938 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9939 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9940 gint selected_zoom, default_zoom;
9942 VikTrwLayer *vtl = pass_along[0];
9943 VikLayersPanel *vlp = pass_along[1];
9945 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9946 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9948 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9952 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9954 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9955 int num_maps = g_list_length(vmls);
9958 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
9962 // Convert from list of vmls to list of names. Allowing the user to select one of them
9963 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9964 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9966 gchar **np = map_names;
9967 VikMapsLayer **lp = map_layers;
9969 for (i = 0; i < num_maps; i++) {
9970 vml = (VikMapsLayer *)(vmls->data);
9972 *np++ = vik_maps_layer_get_map_label(vml);
9975 // Mark end of the array lists
9979 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9980 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
9981 if (cur_zoom == zoom_vals[default_zoom])
9984 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
9986 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
9989 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9992 for (i = 0; i < num_maps; i++)
9993 g_free(map_names[i]);
10001 /**** lowest waypoint number calculation ***/
10002 static gint highest_wp_number_name_to_number(const gchar *name) {
10003 if ( strlen(name) == 3 ) {
10004 int n = atoi(name);
10005 if ( n < 100 && name[0] != '0' )
10007 if ( n < 10 && name[0] != '0' )
10015 static void highest_wp_number_reset(VikTrwLayer *vtl)
10017 vtl->highest_wp_number = -1;
10020 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10022 /* if is bigger that top, add it */
10023 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10024 if ( new_wp_num > vtl->highest_wp_number )
10025 vtl->highest_wp_number = new_wp_num;
10028 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10030 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10031 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10032 if ( vtl->highest_wp_number == old_wp_num ) {
10034 vtl->highest_wp_number--;
10036 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10037 /* search down until we find something that *does* exist */
10039 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10040 vtl->highest_wp_number--;
10041 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10046 /* get lowest unused number */
10047 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10050 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10052 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10053 return g_strdup(buf);