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 #include "viktrwlayer_tracklist.h"
40 #include "viktrwlayer_waypointlist.h"
41 #ifdef VIK_CONFIG_GEOTAG
42 #include "viktrwlayer_geotag.h"
43 #include "geotag_exif.h"
45 #include "garminsymbols.h"
46 #include "thumbnails.h"
47 #include "background.h"
52 #include "geonamessearch.h"
53 #ifdef VIK_CONFIG_OPENSTREETMAP
54 #include "osm-traces.h"
57 #include "datasources.h"
58 #include "datasource_gps.h"
59 #include "vikexttool_datasources.h"
63 #include "vikrouting.h"
65 #include "icons/icons.h"
79 #include <gdk/gdkkeysyms.h>
81 #include <glib/gstdio.h>
82 #include <glib/gi18n.h>
84 #define VIK_TRW_LAYER_TRACK_GC 6
85 #define VIK_TRW_LAYER_TRACK_GCS 10
86 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
87 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
88 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
89 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
90 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
91 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
93 #define DRAWMODE_BY_TRACK 0
94 #define DRAWMODE_BY_SPEED 1
95 #define DRAWMODE_ALL_SAME_COLOR 2
96 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
97 // as we are (re)calculating the colour for every point
102 /* this is how it knows when you click if you are clicking close to a trackpoint. */
103 #define TRACKPOINT_SIZE_APPROX 5
104 #define WAYPOINT_SIZE_APPROX 5
106 #define MIN_STOP_LENGTH 15
107 #define MAX_STOP_LENGTH 86400
108 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
109 /* this is multiplied by user-inputted value from 1-100. */
111 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115 FS_XX_SMALL = 0, // 'xx-small'
118 FS_MEDIUM, // DEFAULT
125 struct _VikTrwLayer {
128 GHashTable *tracks_iters;
130 GHashTable *routes_iters;
131 GHashTable *waypoints_iters;
132 GHashTable *waypoints;
133 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
134 gboolean tracks_visible, routes_visible, waypoints_visible;
135 LatLonBBox waypoints_bbox;
137 gboolean track_draw_labels;
140 guint8 drawpoints_size;
141 guint8 drawelevation;
142 guint8 elevation_factor;
146 guint8 drawdirections;
147 guint8 drawdirections_size;
148 guint8 line_thickness;
149 guint8 bg_line_thickness;
150 vik_layer_sort_order_t track_sort_order;
152 PangoLayout *tracklabellayout;
153 font_size_t track_font_size;
154 gchar *track_fsize_str;
158 gboolean wp_draw_symbols;
159 font_size_t wp_font_size;
161 vik_layer_sort_order_t wp_sort_order;
163 gdouble track_draw_speed_factor;
165 GdkGC *track_1color_gc;
166 GdkColor track_color;
167 GdkGC *current_track_gc;
168 // Separate GC for a track's potential new point as drawn via separate method
169 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
170 GdkGC *current_track_newpoint_gc;
171 GdkGC *track_bg_gc; GdkColor track_bg_color;
172 GdkGC *waypoint_gc; GdkColor waypoint_color;
173 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
174 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
176 GdkFont *waypoint_font;
177 VikTrack *current_track; // ATM shared between new tracks and new routes
178 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
179 gboolean draw_sync_done;
180 gboolean draw_sync_do;
182 VikCoordMode coord_mode;
184 /* wp editing tool */
185 VikWaypoint *current_wp;
186 gpointer current_wp_id;
188 gboolean waypoint_rightclick;
190 /* track editing tool */
192 VikTrack *current_tp_track;
193 gpointer current_tp_id;
194 VikTrwLayerTpwin *tpwin;
196 /* track editing tool -- more specifically, moving tps */
199 /* route finder tool */
200 gboolean route_finder_started;
201 VikCoord route_finder_coord;
202 gboolean route_finder_check_added_track;
203 VikTrack *route_finder_added_track;
204 VikTrack *route_finder_current_track;
205 gboolean route_finder_append;
212 guint16 image_cache_size;
214 /* for waypoint text */
215 PangoLayout *wplabellayout;
217 gboolean has_verified_thumbnails;
219 GtkMenu *wp_right_click_menu;
220 GtkMenu *track_right_click_menu;
223 VikStdLayerMenuItem menu_selection;
225 gint highest_wp_number;
228 GtkWidget *tracks_analysis_dialog;
231 /* A caached waypoint image. */
234 gchar *image; /* filename */
237 struct DrawingParams {
242 guint16 width, height;
243 gdouble cc; // Cosine factor in track directions
244 gdouble ss; // Sine factor in track directions
245 const VikCoord *center;
246 gboolean one_zone, lat_lon;
247 gdouble ce1, ce2, cn1, cn2;
251 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
253 static void trw_layer_delete_item ( gpointer pass_along[6] );
254 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
255 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
257 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
258 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
260 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
261 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
263 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
264 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
266 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
267 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
268 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
269 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
270 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
271 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
272 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
273 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
274 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
275 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
276 static void trw_layer_append_track ( gpointer pass_along[6] );
277 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
278 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
279 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
280 static void trw_layer_split_segments ( gpointer pass_along[6] );
281 static void trw_layer_delete_point_selected ( gpointer pass_along[6] );
282 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
283 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
284 static void trw_layer_reverse ( gpointer pass_along[6] );
285 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
286 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
287 static void trw_layer_show_picture ( gpointer pass_along[6] );
288 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
290 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
291 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
292 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
293 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
294 static void trw_layer_new_wp ( gpointer lav[2] );
295 static void trw_layer_new_track ( gpointer lav[2] );
296 static void trw_layer_new_route ( gpointer lav[2] );
297 static void trw_layer_finish_track ( gpointer lav[2] );
298 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
299 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
300 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
301 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
302 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
303 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
304 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
305 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
306 #ifdef VIK_CONFIG_GEOTAG
307 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
308 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
309 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
310 static void trw_layer_geotagging ( gpointer lav[2] );
312 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
313 static void trw_layer_acquire_routing_cb ( gpointer lav[2] );
314 static void trw_layer_acquire_url_cb ( gpointer lav[2] );
315 #ifdef VIK_CONFIG_OPENSTREETMAP
316 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
317 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] );
319 #ifdef VIK_CONFIG_GEOCACHES
320 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
322 #ifdef VIK_CONFIG_GEOTAG
323 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
325 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
326 static void trw_layer_gps_upload ( gpointer lav[2] );
328 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] );
329 static void trw_layer_track_list_dialog ( gpointer lav[2] );
330 static void trw_layer_waypoint_list_dialog ( gpointer lav[2] );
332 // Specific route versions:
333 // Most track handling functions can handle operating on the route list
334 // However these ones are easier in separate functions
335 static void trw_layer_auto_routes_view ( gpointer lav[2] );
336 static void trw_layer_delete_all_routes ( gpointer lav[2] );
337 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
340 static void trw_layer_properties_item ( gpointer pass_along[7] );
341 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
342 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
343 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] );
345 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
346 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
347 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
349 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
350 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
351 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
352 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
354 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
355 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
356 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
357 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
358 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
359 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
360 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
361 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
362 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
363 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
364 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
365 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
366 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
367 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
368 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
369 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
370 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
371 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
372 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
373 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
374 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
375 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
376 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
378 static void cached_pixbuf_free ( CachedPixbuf *cp );
379 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
381 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
382 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
384 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
385 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
387 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
388 static void highest_wp_number_reset(VikTrwLayer *vtl);
389 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
390 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
392 // Note for the following tool GtkRadioActionEntry texts:
393 // the very first text value is an internal name not displayed anywhere
394 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
395 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
396 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
397 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
398 static VikToolInterface trw_layer_tools[] = {
399 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
400 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
401 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
403 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
405 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
406 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
407 (VikToolMouseFunc) tool_new_track_click,
408 (VikToolMouseMoveFunc) tool_new_track_move,
409 (VikToolMouseFunc) tool_new_track_release,
410 (VikToolKeyFunc) tool_new_track_key_press,
411 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
412 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
414 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
415 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
416 (VikToolMouseFunc) tool_new_route_click,
417 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
418 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
419 (VikToolKeyFunc) tool_new_track_key_press, // -/#
420 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
421 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
423 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
424 (VikToolConstructorFunc) tool_edit_waypoint_create,
425 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
427 (VikToolMouseFunc) tool_edit_waypoint_click,
428 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
429 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
431 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
433 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
434 (VikToolConstructorFunc) tool_edit_trackpoint_create,
435 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
437 (VikToolMouseFunc) tool_edit_trackpoint_click,
438 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
439 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
441 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
443 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
444 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
445 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
447 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
449 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
450 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
451 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
453 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
457 TOOL_CREATE_WAYPOINT=0,
461 TOOL_EDIT_TRACKPOINT,
467 /****** PARAMETERS ******/
469 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced") };
470 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV };
472 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
473 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
475 #define MIN_POINT_SIZE 2
476 #define MAX_POINT_SIZE 10
478 #define MIN_ARROW_SIZE 3
479 #define MAX_ARROW_SIZE 20
481 static VikLayerParamScale params_scales[] = {
482 /* min max step digits */
483 { 1, 10, 1, 0 }, /* line_thickness */
484 { 0, 100, 1, 0 }, /* track draw speed factor */
485 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
486 /* 5 * step == how much to turn */
487 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
488 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
489 { 5, 500, 5, 0 }, // 5: image cache_size - " "
490 { 0, 8, 1, 0 }, // 6: Background line thickness
491 { 1, 64, 1, 0 }, /* wpsize */
492 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
493 { 1, 100, 1, 0 }, // 9: elevation factor
494 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
495 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
498 static gchar* params_font_sizes[] = {
499 N_("Extra Extra Small"),
505 N_("Extra Extra Large"),
508 // Needs to align with vik_layer_sort_order_t
509 static gchar* params_sort_order[] = {
511 N_("Name Ascending"),
512 N_("Name Descending"),
516 static VikLayerParamData black_color_default ( void ) {
517 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
519 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
520 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
521 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
522 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
523 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
524 static VikLayerParamData trackbgcolor_default ( void ) {
525 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
527 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
528 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
529 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
531 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
532 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
533 static VikLayerParamData wptextcolor_default ( void ) {
534 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
536 static VikLayerParamData wpbgcolor_default ( void ) {
537 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
539 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
540 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
542 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
543 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
544 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
546 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
548 VikLayerParam trw_layer_params[] = {
549 { 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 },
550 { 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 },
551 { 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 },
553 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
554 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
555 { 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 },
556 { 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 },
557 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
558 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
559 { 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 },
560 { 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 },
561 { 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 },
562 { 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 },
563 { 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 },
564 { 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 },
565 { 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 },
566 { 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 },
567 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
568 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 },
569 { 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 },
571 { 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 },
572 { 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 },
573 { 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,
574 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
575 { 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 },
577 { 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 },
578 { 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 },
579 { 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 },
580 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
581 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
582 { 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 },
583 { 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 },
584 { 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 },
585 { 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 },
586 { 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 },
588 { 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 },
589 { 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 },
590 { 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 },
591 { 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 },
594 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
596 // Sublayer visibilities
639 *** 1) Add to trw_layer_params and enumeration
640 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
643 /****** END PARAMETERS ******/
645 /* Layer Interface function definitions */
646 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
647 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
648 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
649 static void trw_layer_free ( VikTrwLayer *trwlayer );
650 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
651 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
652 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
653 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
654 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
655 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
656 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
657 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
658 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
659 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
660 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
661 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
662 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
663 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
664 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
665 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
666 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
667 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
668 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
669 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
670 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
671 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
672 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
673 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
674 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
675 /* End Layer Interface function definitions */
677 VikLayerInterface vik_trw_layer_interface = {
684 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
688 params_groups, /* params_groups */
689 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
693 (VikLayerFuncCreate) trw_layer_create,
694 (VikLayerFuncRealize) trw_layer_realize,
695 (VikLayerFuncPostRead) trw_layer_post_read,
696 (VikLayerFuncFree) trw_layer_free,
698 (VikLayerFuncProperties) NULL,
699 (VikLayerFuncDraw) trw_layer_draw,
700 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
702 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
703 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
705 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
706 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
708 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
709 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
710 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
711 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
712 (VikLayerFuncLayerSelected) trw_layer_selected,
714 (VikLayerFuncMarshall) trw_layer_marshall,
715 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
717 (VikLayerFuncSetParam) trw_layer_set_param,
718 (VikLayerFuncGetParam) trw_layer_get_param,
720 (VikLayerFuncReadFileData) a_gpspoint_read_file,
721 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
723 (VikLayerFuncDeleteItem) trw_layer_del_item,
724 (VikLayerFuncCutItem) trw_layer_cut_item,
725 (VikLayerFuncCopyItem) trw_layer_copy_item,
726 (VikLayerFuncPasteItem) trw_layer_paste_item,
727 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
729 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
731 (VikLayerFuncSelectClick) trw_layer_select_click,
732 (VikLayerFuncSelectMove) trw_layer_select_move,
733 (VikLayerFuncSelectRelease) trw_layer_select_release,
734 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
737 GType vik_trw_layer_get_type ()
739 static GType vtl_type = 0;
743 static const GTypeInfo vtl_info =
745 sizeof (VikTrwLayerClass),
746 NULL, /* base_init */
747 NULL, /* base_finalize */
748 NULL, /* class init */
749 NULL, /* class_finalize */
750 NULL, /* class_data */
751 sizeof (VikTrwLayer),
753 NULL /* instance init */
755 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
761 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
763 static gpointer pass_along[6];
769 pass_along[1] = NULL;
770 pass_along[2] = GINT_TO_POINTER (subtype);
771 pass_along[3] = sublayer;
772 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
773 pass_along[5] = NULL;
775 trw_layer_delete_item ( pass_along );
778 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
780 static gpointer pass_along[6];
786 pass_along[1] = NULL;
787 pass_along[2] = GINT_TO_POINTER (subtype);
788 pass_along[3] = sublayer;
789 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
790 pass_along[5] = NULL;
792 trw_layer_copy_item_cb(pass_along);
793 trw_layer_cut_item_cb(pass_along);
796 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
798 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
799 gint subtype = GPOINTER_TO_INT (pass_along[2]);
800 gpointer * sublayer = pass_along[3];
804 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
808 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
809 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
810 if ( wp && wp->name )
813 name = NULL; // Broken :(
815 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
816 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
817 if ( trk && trk->name )
820 name = NULL; // Broken :(
823 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
824 if ( trk && trk->name )
827 name = NULL; // Broken :(
830 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
831 subtype, len, name, data);
835 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
837 trw_layer_copy_item_cb(pass_along);
838 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
839 trw_layer_delete_item(pass_along);
842 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
844 // Slightly cheating method, routing via the panels capability
845 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
848 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
858 GByteArray *ba = g_byte_array_new ();
860 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
861 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
862 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
863 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
865 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
868 g_byte_array_append ( ba, id, il );
876 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
883 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
887 w = vik_waypoint_unmarshall ( item, len );
888 // When copying - we'll create a new name based on the original
889 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
890 vik_trw_layer_add_waypoint ( vtl, name, w );
891 waypoint_convert (NULL, w, &vtl->coord_mode);
894 trw_layer_calculate_bounds_waypoints ( vtl );
896 // Consider if redraw necessary for the new item
897 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
898 vik_layer_emit_update ( VIK_LAYER(vtl) );
901 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
905 t = vik_track_unmarshall ( item, len );
906 // When copying - we'll create a new name based on the original
907 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
908 vik_trw_layer_add_track ( vtl, name, t );
909 vik_track_convert (t, vtl->coord_mode);
912 // Consider if redraw necessary for the new item
913 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
914 vik_layer_emit_update ( VIK_LAYER(vtl) );
917 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
921 t = vik_track_unmarshall ( item, len );
922 // When copying - we'll create a new name based on the original
923 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
924 vik_trw_layer_add_route ( vtl, name, t );
925 vik_track_convert (t, vtl->coord_mode);
928 // Consider if redraw necessary for the new item
929 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
930 vik_layer_emit_update ( VIK_LAYER(vtl) );
936 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
943 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
947 case PARAM_TV: vtl->tracks_visible = data.b; break;
948 case PARAM_WV: vtl->waypoints_visible = data.b; break;
949 case PARAM_RV: vtl->routes_visible = data.b; break;
950 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
951 case PARAM_TLFONTSIZE:
952 if ( data.u < FS_NUM_SIZES ) {
953 vtl->track_font_size = data.u;
954 g_free ( vtl->track_fsize_str );
955 switch ( vtl->track_font_size ) {
956 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
957 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
958 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
959 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
960 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
961 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
962 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
966 case PARAM_DM: vtl->drawmode = data.u; break;
968 vtl->track_color = data.c;
969 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
971 case PARAM_DP: vtl->drawpoints = data.b; break;
973 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
974 vtl->drawpoints_size = data.u;
976 case PARAM_DE: vtl->drawelevation = data.b; break;
977 case PARAM_DS: vtl->drawstops = data.b; break;
978 case PARAM_DL: vtl->drawlines = data.b; break;
979 case PARAM_DD: vtl->drawdirections = data.b; break;
981 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
982 vtl->drawdirections_size = data.u;
984 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
985 vtl->stop_length = data.u;
987 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
988 vtl->elevation_factor = data.u;
990 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
992 vtl->line_thickness = data.u;
993 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
996 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
998 vtl->bg_line_thickness = data.u;
999 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1003 vtl->track_bg_color = data.c;
1004 if ( vtl->track_bg_gc )
1005 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1007 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1008 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1009 case PARAM_DLA: vtl->drawlabels = data.b; break;
1010 case PARAM_DI: vtl->drawimages = data.b; break;
1011 case PARAM_IS: if ( data.u != vtl->image_size )
1013 vtl->image_size = data.u;
1014 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1015 g_queue_free ( vtl->image_cache );
1016 vtl->image_cache = g_queue_new ();
1019 case PARAM_IA: vtl->image_alpha = data.u; break;
1020 case PARAM_ICS: vtl->image_cache_size = data.u;
1021 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1022 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1025 vtl->waypoint_color = data.c;
1026 if ( vtl->waypoint_gc )
1027 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1030 vtl->waypoint_text_color = data.c;
1031 if ( vtl->waypoint_text_gc )
1032 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1035 vtl->waypoint_bg_color = data.c;
1036 if ( vtl->waypoint_bg_gc )
1037 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1040 vtl->wpbgand = data.b;
1041 if ( vtl->waypoint_bg_gc )
1042 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1044 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1045 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1046 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1047 case PARAM_WPFONTSIZE:
1048 if ( data.u < FS_NUM_SIZES ) {
1049 vtl->wp_font_size = data.u;
1050 g_free ( vtl->wp_fsize_str );
1051 switch ( vtl->wp_font_size ) {
1052 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1053 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1054 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1055 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1056 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1057 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1058 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1062 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1067 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1069 VikLayerParamData rv;
1072 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1073 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1074 case PARAM_RV: rv.b = vtl->routes_visible; break;
1075 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1076 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1077 case PARAM_DM: rv.u = vtl->drawmode; break;
1078 case PARAM_TC: rv.c = vtl->track_color; break;
1079 case PARAM_DP: rv.b = vtl->drawpoints; break;
1080 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1081 case PARAM_DE: rv.b = vtl->drawelevation; break;
1082 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1083 case PARAM_DS: rv.b = vtl->drawstops; break;
1084 case PARAM_SL: rv.u = vtl->stop_length; break;
1085 case PARAM_DL: rv.b = vtl->drawlines; break;
1086 case PARAM_DD: rv.b = vtl->drawdirections; break;
1087 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1088 case PARAM_LT: rv.u = vtl->line_thickness; break;
1089 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1090 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1091 case PARAM_DI: rv.b = vtl->drawimages; break;
1092 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1093 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1094 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1095 case PARAM_IS: rv.u = vtl->image_size; break;
1096 case PARAM_IA: rv.u = vtl->image_alpha; break;
1097 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1098 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1099 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1100 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1101 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1102 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1103 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1104 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1105 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1106 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1111 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1118 // Use byte arrays to store sublayer data
1119 // much like done elsewhere e.g. vik_layer_marshall_params()
1120 GByteArray *ba = g_byte_array_new ( );
1125 guint object_length;
1128 // the length of the item
1129 // the sublayer type of item
1130 // the the actual item
1131 #define tlm_append(object_pointer, size, type) \
1133 object_length = (size); \
1134 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1135 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1136 g_byte_array_append ( ba, (object_pointer), object_length );
1138 // Layer parameters first
1139 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1140 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1141 g_byte_array_append ( ba, pd, pl );
1144 // Now sublayer data
1145 GHashTableIter iter;
1146 gpointer key, value;
1149 g_hash_table_iter_init ( &iter, vtl->waypoints );
1150 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1151 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1152 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1157 g_hash_table_iter_init ( &iter, vtl->tracks );
1158 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1159 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1160 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1165 g_hash_table_iter_init ( &iter, vtl->routes );
1166 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1167 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1168 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1178 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1180 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1182 gint consumed_length;
1184 // First the overall layer parameters
1185 memcpy(&pl, data, sizeof(pl));
1187 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1190 consumed_length = pl;
1191 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1193 #define tlm_size (*(gint *)data)
1194 // See marshalling above for order of how this is written
1196 data += sizeof_len_and_subtype + tlm_size;
1198 // Now the individual sublayers:
1200 while ( *data && consumed_length < len ) {
1201 // Normally four extra bytes at the end of the datastream
1202 // (since it's a GByteArray and that's where it's length is stored)
1203 // So only attempt read when there's an actual block of sublayer data
1204 if ( consumed_length + tlm_size < len ) {
1206 // Reuse pl to read the subtype from the data stream
1207 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1209 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1210 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1211 gchar *name = g_strdup ( trk->name );
1212 vik_trw_layer_add_track ( vtl, name, trk );
1215 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1216 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1217 gchar *name = g_strdup ( wp->name );
1218 vik_trw_layer_add_waypoint ( vtl, name, wp );
1221 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1222 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1223 gchar *name = g_strdup ( trk->name );
1224 vik_trw_layer_add_route ( vtl, name, trk );
1228 consumed_length += tlm_size + sizeof_len_and_subtype;
1231 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1233 // Not stored anywhere else so need to regenerate
1234 trw_layer_calculate_bounds_waypoints ( vtl );
1239 // Keep interesting hash function at least visible
1241 static guint strcase_hash(gconstpointer v)
1243 // 31 bit hash function
1246 gchar s[128]; // malloc is too slow for reading big files
1249 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1250 p[i] = toupper(t[i]);
1256 for (p += 1; *p != '\0'; p++)
1257 h = (h << 5) - h + *p;
1264 // Stick a 1 at the end of the function name to make it more unique
1265 // thus more easily searchable in a simple text editor
1266 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1268 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1269 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1271 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1272 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1274 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1275 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1276 // and with normal PC processing capabilities - it has negligibile performance impact
1277 // This also minimized the amount of rework - as the management of the hash tables already exists.
1279 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1280 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1281 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1283 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1284 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1285 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1286 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1287 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1288 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1290 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1292 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1294 // Param settings that are not available via the GUI
1295 // Force to on after processing params (which defaults them to off with a zero value)
1296 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1298 rv->draw_sync_done = TRUE;
1299 rv->draw_sync_do = TRUE;
1300 // Everything else is 0, FALSE or NULL
1306 static void trw_layer_free ( VikTrwLayer *trwlayer )
1308 g_hash_table_destroy(trwlayer->waypoints);
1309 g_hash_table_destroy(trwlayer->waypoints_iters);
1310 g_hash_table_destroy(trwlayer->tracks);
1311 g_hash_table_destroy(trwlayer->tracks_iters);
1312 g_hash_table_destroy(trwlayer->routes);
1313 g_hash_table_destroy(trwlayer->routes_iters);
1315 /* ODC: replace with GArray */
1316 trw_layer_free_track_gcs ( trwlayer );
1318 if ( trwlayer->wp_right_click_menu )
1319 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1321 if ( trwlayer->track_right_click_menu )
1322 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1324 if ( trwlayer->tracklabellayout != NULL)
1325 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1327 if ( trwlayer->wplabellayout != NULL)
1328 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1330 if ( trwlayer->waypoint_gc != NULL )
1331 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1333 if ( trwlayer->waypoint_text_gc != NULL )
1334 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1336 if ( trwlayer->waypoint_bg_gc != NULL )
1337 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1339 g_free ( trwlayer->wp_fsize_str );
1340 g_free ( trwlayer->track_fsize_str );
1342 if ( trwlayer->tpwin != NULL )
1343 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1345 if ( trwlayer->tracks_analysis_dialog != NULL )
1346 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1348 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1349 g_queue_free ( trwlayer->image_cache );
1352 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1356 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1357 dp->xmpp = vik_viewport_get_xmpp ( vp );
1358 dp->ympp = vik_viewport_get_ympp ( vp );
1359 dp->width = vik_viewport_get_width ( vp );
1360 dp->height = vik_viewport_get_height ( vp );
1361 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1362 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1364 dp->center = vik_viewport_get_center ( vp );
1365 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1366 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1371 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1372 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1373 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1375 dp->ce1 = dp->center->east_west-w2;
1376 dp->ce2 = dp->center->east_west+w2;
1377 dp->cn1 = dp->center->north_south-h2;
1378 dp->cn2 = dp->center->north_south+h2;
1379 } else if ( dp->lat_lon ) {
1380 VikCoord upperleft, bottomright;
1381 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1382 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1383 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1384 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1385 dp->ce1 = upperleft.east_west;
1386 dp->ce2 = bottomright.east_west;
1387 dp->cn1 = bottomright.north_south;
1388 dp->cn2 = upperleft.north_south;
1391 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1395 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1396 * Here a simple traffic like light colour system is used:
1397 * . slow points are red
1398 * . average is yellow
1399 * . fast points are green
1401 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1404 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1405 if ( average_speed > 0 ) {
1406 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1407 if ( rv < low_speed )
1408 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1409 else if ( rv > high_speed )
1410 return VIK_TRW_LAYER_TRACK_GC_FAST;
1412 return VIK_TRW_LAYER_TRACK_GC_AVER;
1415 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1418 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1420 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1421 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1422 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1423 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1427 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1429 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1431 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1432 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1434 // Fallback if parse failure
1435 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1437 g_free ( label_markup );
1439 gint label_x, label_y;
1441 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1443 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1444 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1448 * distance_in_preferred_units:
1449 * @dist: The source distance in standard SI Units (i.e. metres)
1451 * TODO: This is a generic function that could be moved into globals.c or utils.c
1453 * Probably best used if you have a only few conversions to perform.
1454 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1455 * since it will be doing the preference check on each call
1457 * Returns: The distance in the units as specified by the preferences
1459 static gdouble distance_in_preferred_units ( gdouble dist )
1462 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1463 switch (dist_units) {
1464 case VIK_UNITS_DISTANCE_MILES:
1465 mydist = VIK_METERS_TO_MILES(dist);
1467 // VIK_UNITS_DISTANCE_KILOMETRES:
1469 mydist = dist/1000.0;
1476 * trw_layer_draw_dist_labels:
1478 * Draw a few labels along a track at nicely seperated distances
1479 * This might slow things down if there's many tracks being displayed with this on.
1481 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1483 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1484 25.0, 40.0, 50.0, 75.0, 100.0,
1485 150.0, 200.0, 250.0, 500.0, 1000.0};
1487 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1489 // Convert to specified unit to find the friendly breakdown value
1490 dist = distance_in_preferred_units ( dist );
1494 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1495 if ( chunksd[i] > dist ) {
1497 dist = chunksd[index];
1502 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1504 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1505 gdouble dist_i = dist * i;
1507 // Convert distance back into metres for use in finding a trackpoint
1508 switch (dist_units) {
1509 case VIK_UNITS_DISTANCE_MILES:
1510 dist_i = VIK_MILES_TO_METERS(dist_i);
1512 // VIK_UNITS_DISTANCE_KILOMETRES:
1514 dist_i = dist_i*1000.0;
1518 gdouble dist_current = 0.0;
1519 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1520 gdouble dist_next = 0.0;
1521 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1523 gdouble dist_between_tps = fabs (dist_next - dist_current);
1524 gdouble ratio = 0.0;
1525 // Prevent division by 0 errors
1526 if ( dist_between_tps > 0.0 )
1527 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1529 if ( tp_current && tp_next ) {
1530 // Construct the name based on the distance value
1533 switch (dist_units) {
1534 case VIK_UNITS_DISTANCE_MILES:
1535 units = g_strdup ( _("miles") );
1537 // VIK_UNITS_DISTANCE_KILOMETRES:
1539 units = g_strdup ( _("km") );
1543 // Convert for display
1544 dist_i = distance_in_preferred_units ( dist_i );
1546 // Make the precision of the output related to the unit size.
1548 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1549 else if ( index == 1 )
1550 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1552 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1555 struct LatLon ll_current, ll_next;
1556 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1557 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1559 // positional interpolation
1560 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1561 // but should be good enough over the small scale that I anticipate usage on
1562 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1563 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1565 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1568 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1569 fgcolour = gdk_color_to_string ( &(trk->color) );
1571 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1573 // if highlight mode on, then colour the background in the highlight colour
1575 if ( drawing_highlight )
1576 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1578 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1580 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1582 g_free ( fgcolour );
1583 g_free ( bgcolour );
1590 * trw_layer_draw_track_name_labels:
1592 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1594 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1597 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1598 fgcolour = gdk_color_to_string ( &(trk->color) );
1600 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1602 // if highlight mode on, then colour the background in the highlight colour
1604 if ( drawing_highlight )
1605 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1607 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1609 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1611 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1612 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1613 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1614 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1615 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1616 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1618 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1620 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1623 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1624 // No other labels to draw
1627 GList *ending = g_list_last ( trk->trackpoints );
1628 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1629 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1631 gboolean done_start_end = FALSE;
1633 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1634 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1636 // This number can be configured via the settings if you really want to change it
1637 gdouble distance_diff;
1638 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1639 distance_diff = 100.0; // Metres
1641 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1642 // Start and end 'close' together so only draw one label at an average location
1643 gint x1, x2, y1, y2;
1644 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1645 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1647 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1649 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1650 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1653 done_start_end = TRUE;
1657 if ( ! done_start_end ) {
1658 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1659 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1660 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1661 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1662 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1663 g_free ( name_start );
1665 // Don't draw end label if this is the one being created
1666 if ( trk != dp->vtl->current_track ) {
1667 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1668 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1669 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1670 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1671 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1672 g_free ( name_end );
1677 g_free ( fgcolour );
1678 g_free ( bgcolour );
1682 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1684 if ( ! track->visible )
1687 /* TODO: this function is a mess, get rid of any redundancy */
1688 GList *list = track->trackpoints;
1690 gboolean useoldvals = TRUE;
1692 gboolean drawpoints;
1694 gboolean drawelevation;
1695 gdouble min_alt, max_alt, alt_diff = 0;
1697 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1698 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1701 if ( dp->vtl->drawelevation )
1703 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1704 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1705 alt_diff = max_alt - min_alt;
1708 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1709 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1710 trw_layer_draw_track ( id, track, dp, TRUE );
1712 if ( draw_track_outline )
1713 drawpoints = drawstops = FALSE;
1715 drawpoints = dp->vtl->drawpoints;
1716 drawstops = dp->vtl->drawstops;
1719 gboolean drawing_highlight = FALSE;
1720 /* Current track - used for creation */
1721 if ( track == dp->vtl->current_track )
1722 main_gc = dp->vtl->current_track_gc;
1724 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1725 /* Draw all tracks of the layer in special colour */
1726 /* if track is member of selected layer or is the current selected track
1727 then draw in the highlight colour.
1728 NB this supercedes the drawmode */
1729 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1730 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1731 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1732 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1733 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1734 drawing_highlight = TRUE;
1737 if ( !drawing_highlight ) {
1738 // Still need to figure out the gc according to the drawing mode:
1739 switch ( dp->vtl->drawmode ) {
1740 case DRAWMODE_BY_TRACK:
1741 if ( dp->vtl->track_1color_gc )
1742 g_object_unref ( dp->vtl->track_1color_gc );
1743 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1744 main_gc = dp->vtl->track_1color_gc;
1747 // Mostly for DRAWMODE_ALL_SAME_COLOR
1748 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1749 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1756 int x, y, oldx, oldy;
1757 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1759 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1761 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1763 // Draw the first point as something a bit different from the normal points
1764 // ATM it's slightly bigger and a triangle
1766 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1767 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1773 gdouble average_speed = 0.0;
1774 gdouble low_speed = 0.0;
1775 gdouble high_speed = 0.0;
1776 // If necessary calculate these values - which is done only once per track redraw
1777 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1778 // the percentage factor away from the average speed determines transistions between the levels
1779 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1780 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1781 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1784 while ((list = g_list_next(list)))
1786 tp = VIK_TRACKPOINT(list->data);
1787 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1789 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1790 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1791 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1792 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1793 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1795 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1798 * If points are the same in display coordinates, don't draw.
1800 if ( useoldvals && x == oldx && y == oldy )
1802 // Still need to process points to ensure 'stops' are drawn if required
1803 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1804 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1805 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 );
1810 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1811 if ( drawpoints || dp->vtl->drawlines ) {
1812 // setup main_gc for both point and line drawing
1813 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1814 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 ) );
1818 if ( drawpoints && ! draw_track_outline )
1823 * The concept of drawing stops is that a trackpoint
1824 * that is if the next trackpoint has a timestamp far into
1825 * the future, we draw a circle of 6x trackpoint size,
1826 * instead of a rectangle of 2x trackpoint size.
1827 * This is drawn first so the trackpoint will be drawn on top
1830 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1831 /* Stop point. Draw 6x circle. Always in redish colour */
1832 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 );
1834 /* Regular point - draw 2x square. */
1835 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1838 /* Final point - draw 4x circle. */
1839 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 );
1842 if ((!tp->newsegment) && (dp->vtl->drawlines))
1845 /* UTM only: zone check */
1846 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1847 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1850 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1852 if ( draw_track_outline ) {
1853 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1857 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1859 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1861 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1866 tmp[1].y = oldy-FIXALTITUDE(list->data);
1868 tmp[2].y = y-FIXALTITUDE(list->next->data);
1873 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1874 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1876 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1877 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1879 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1884 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1885 // Draw an arrow at the mid point to show the direction of the track
1886 // Code is a rework from vikwindow::draw_ruler()
1887 gint midx = (oldx + x) / 2;
1888 gint midy = (oldy + y) / 2;
1890 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1891 // Avoid divide by zero and ensure at least 1 pixel big
1893 gdouble dx = (oldx - midx) / len;
1894 gdouble dy = (oldy - midy) / len;
1895 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1896 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1906 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1908 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1909 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1911 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1913 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1914 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 ));
1918 * If points are the same in display coordinates, don't draw.
1920 if ( x != oldx || y != oldy )
1922 if ( draw_track_outline )
1923 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1925 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1931 * If points are the same in display coordinates, don't draw.
1933 if ( x != oldx && y != oldy )
1935 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1936 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1944 // Labels drawn after the trackpoints, so the labels are on top
1945 if ( dp->vtl->track_draw_labels ) {
1946 if ( track->max_number_dist_labels > 0 ) {
1947 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
1950 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
1951 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
1957 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
1959 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
1960 trw_layer_draw_track ( id, track, dp, FALSE );
1964 static void cached_pixbuf_free ( CachedPixbuf *cp )
1966 g_object_unref ( G_OBJECT(cp->pixbuf) );
1967 g_free ( cp->image );
1970 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1972 return strcmp ( cp->image, name );
1975 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
1978 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
1979 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1980 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1983 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1985 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1987 if ( wp->image && dp->vtl->drawimages )
1989 GdkPixbuf *pixbuf = NULL;
1992 if ( dp->vtl->image_alpha == 0)
1995 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1997 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2000 gchar *image = wp->image;
2001 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2002 if ( ! regularthumb )
2004 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2005 image = "\x12\x00"; /* this shouldn't occur naturally. */
2009 CachedPixbuf *cp = NULL;
2010 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2011 if ( dp->vtl->image_size == 128 )
2012 cp->pixbuf = regularthumb;
2015 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2016 g_assert ( cp->pixbuf );
2017 g_object_unref ( G_OBJECT(regularthumb) );
2019 cp->image = g_strdup ( image );
2021 /* needed so 'click picture' tool knows how big the pic is; we don't
2022 * store it in cp because they may have been freed already. */
2023 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2024 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2026 g_queue_push_head ( dp->vtl->image_cache, cp );
2027 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2028 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2030 pixbuf = cp->pixbuf;
2034 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2040 w = gdk_pixbuf_get_width ( pixbuf );
2041 h = gdk_pixbuf_get_height ( pixbuf );
2043 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2045 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2046 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2047 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2048 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2049 // Highlighted - so draw a little border around the chosen one
2050 // single line seems a little weak so draw 2 of them
2051 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2052 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2053 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2054 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2057 if ( dp->vtl->image_alpha == 255 )
2058 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2060 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2062 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2066 // Draw appropriate symbol - either symbol image or simple types
2067 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2068 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 );
2070 else if ( wp == dp->vtl->current_wp ) {
2071 switch ( dp->vtl->wp_symbol ) {
2072 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;
2073 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;
2074 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;
2075 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 );
2076 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 );
2080 switch ( dp->vtl->wp_symbol ) {
2081 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;
2082 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;
2083 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;
2084 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 );
2085 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;
2089 if ( dp->vtl->drawlabels )
2091 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2092 gint label_x, label_y;
2094 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2096 // Could this stored in the waypoint rather than recreating each pass?
2097 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2099 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2100 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2102 // Fallback if parse failure
2103 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2105 g_free ( wp_label_markup );
2107 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2108 label_x = x - width/2;
2109 if ( wp->symbol_pixbuf )
2110 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2112 label_y = y - dp->vtl->wp_size - height - 2;
2114 /* if highlight mode on, then draw background text in highlight colour */
2115 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2116 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2117 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2118 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2119 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2121 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2124 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2126 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2131 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2133 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2134 trw_layer_draw_waypoint ( id, wp, dp );
2138 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2140 static struct DrawingParams dp;
2141 g_assert ( l != NULL );
2143 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2145 if ( l->tracks_visible )
2146 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2148 if ( l->routes_visible )
2149 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2151 if (l->waypoints_visible)
2152 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2155 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2158 if ( vtl->track_bg_gc )
2160 g_object_unref ( vtl->track_bg_gc );
2161 vtl->track_bg_gc = NULL;
2163 if ( vtl->track_1color_gc )
2165 g_object_unref ( vtl->track_1color_gc );
2166 vtl->track_1color_gc = NULL;
2168 if ( vtl->current_track_gc )
2170 g_object_unref ( vtl->current_track_gc );
2171 vtl->current_track_gc = NULL;
2173 if ( vtl->current_track_newpoint_gc )
2175 g_object_unref ( vtl->current_track_newpoint_gc );
2176 vtl->current_track_newpoint_gc = NULL;
2179 if ( ! vtl->track_gc )
2181 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2182 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2183 g_array_free ( vtl->track_gc, TRUE );
2184 vtl->track_gc = NULL;
2187 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2189 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2190 gint width = vtl->line_thickness;
2192 if ( vtl->track_gc )
2193 trw_layer_free_track_gcs ( vtl );
2195 if ( vtl->track_bg_gc )
2196 g_object_unref ( vtl->track_bg_gc );
2197 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2199 // Ensure new track drawing heeds line thickness setting
2200 // however always have a minium of 2, as 1 pixel is really narrow
2201 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2203 if ( vtl->current_track_gc )
2204 g_object_unref ( vtl->current_track_gc );
2205 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2206 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2208 // 'newpoint' gc is exactly the same as the current track gc
2209 if ( vtl->current_track_newpoint_gc )
2210 g_object_unref ( vtl->current_track_newpoint_gc );
2211 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2212 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2214 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2216 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2217 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2219 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2220 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2221 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2223 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2225 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2228 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2230 VikTrwLayer *rv = trw_layer_new1 ( vp );
2231 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2233 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2234 /* early exit, as the rest is GUI related */
2238 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2239 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2241 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2242 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2244 trw_layer_new_track_gcs ( rv, vp );
2246 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2247 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2248 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2249 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2251 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2253 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2258 #define SMALL_ICON_SIZE 18
2260 * Can accept a null symbol, and may return null value
2262 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2264 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2265 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2266 // So needing a small icon for the treeview may need some resizing:
2267 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2268 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2272 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2274 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2276 GdkPixbuf *pixbuf = NULL;
2278 if ( track->has_color ) {
2279 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2280 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2281 // Here is some magic found to do the conversion
2282 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2283 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2284 ((track->color.green & 0xff00) << 8) |
2285 (track->color.blue & 0xff00);
2287 gdk_pixbuf_fill ( pixbuf, pixel );
2290 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 );
2293 g_object_unref (pixbuf);
2295 *new_iter = *((GtkTreeIter *) pass_along[1]);
2296 if ( track->is_route )
2297 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2299 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2301 if ( ! track->visible )
2302 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2305 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2307 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2309 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 );
2311 *new_iter = *((GtkTreeIter *) pass_along[1]);
2312 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2314 if ( ! wp->visible )
2315 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2318 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2320 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2323 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2325 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2328 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2330 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2333 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2336 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2338 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2339 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2341 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2343 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2346 if ( g_hash_table_size (vtl->routes) > 0 ) {
2347 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2349 pass_along[0] = &(vtl->routes_iter);
2350 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2352 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2354 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2357 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2358 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2360 pass_along[0] = &(vtl->waypoints_iter);
2361 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2363 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2365 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2370 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2374 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2375 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2376 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2377 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2379 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2381 return (t->visible ^= 1);
2385 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2387 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2389 return (t->visible ^= 1);
2393 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2395 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2397 return (t->visible ^= 1);
2406 * Return a property about tracks for this layer
2408 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2410 return vtl->line_thickness;
2413 // Structure to hold multiple track information for a layer
2422 * Build up layer multiple track information via updating the tooltip_tracks structure
2424 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2426 tt->length = tt->length + vik_track_get_length (tr);
2428 // Ensure times are available
2429 if ( tr->trackpoints &&
2430 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2431 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2434 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2435 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2437 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2438 // Hence initialize to the first 'proper' value
2439 if ( tt->start_time == 0 )
2440 tt->start_time = t1;
2441 if ( tt->end_time == 0 )
2444 // Update find the earliest / last times
2445 if ( t1 < tt->start_time )
2446 tt->start_time = t1;
2447 if ( t2 > tt->end_time )
2450 // Keep track of total time
2451 // there maybe gaps within a track (eg segments)
2452 // but this should be generally good enough for a simple indicator
2453 tt->duration = tt->duration + (int)(t2-t1);
2458 * Generate tooltip text for the layer.
2459 * This is relatively complicated as it considers information for
2460 * no tracks, a single track or multiple tracks
2461 * (which may or may not have timing information)
2463 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2474 static gchar tmp_buf[128];
2477 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2479 // Safety check - I think these should always be valid
2480 if ( vtl->tracks && vtl->waypoints ) {
2481 tooltip_tracks tt = { 0.0, 0, 0 };
2482 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2484 GDate* gdate_start = g_date_new ();
2485 g_date_set_time_t (gdate_start, tt.start_time);
2487 GDate* gdate_end = g_date_new ();
2488 g_date_set_time_t (gdate_end, tt.end_time);
2490 if ( g_date_compare (gdate_start, gdate_end) ) {
2491 // Dates differ so print range on separate line
2492 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2493 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2494 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2497 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2498 if ( tt.start_time != 0 )
2499 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2503 if ( tt.length > 0.0 ) {
2504 gdouble len_in_units;
2506 // Setup info dependent on distance units
2507 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2508 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2509 len_in_units = VIK_METERS_TO_MILES(tt.length);
2512 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2513 len_in_units = tt.length/1000.0;
2516 // Timing information if available
2518 if ( tt.duration > 0 ) {
2519 g_snprintf (tbuf1, sizeof(tbuf1),
2520 _(" in %d:%02d hrs:mins"),
2521 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2523 g_snprintf (tbuf2, sizeof(tbuf2),
2524 _("\n%sTotal Length %.1f %s%s"),
2525 tbuf3, len_in_units, tbuf4, tbuf1);
2528 // Put together all the elements to form compact tooltip text
2529 g_snprintf (tmp_buf, sizeof(tmp_buf),
2530 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2531 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2533 g_date_free (gdate_start);
2534 g_date_free (gdate_end);
2541 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2545 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2547 // Very simple tooltip - may expand detail in the future...
2548 static gchar tmp_buf[32];
2549 g_snprintf (tmp_buf, sizeof(tmp_buf),
2551 g_hash_table_size (l->tracks));
2555 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2557 // Very simple tooltip - may expand detail in the future...
2558 static gchar tmp_buf[32];
2559 g_snprintf (tmp_buf, sizeof(tmp_buf),
2561 g_hash_table_size (l->routes));
2566 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2567 // Same tooltip for a route
2568 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2571 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2572 tr = g_hash_table_lookup ( l->tracks, sublayer );
2574 tr = g_hash_table_lookup ( l->routes, sublayer );
2577 // Could be a better way of handling strings - but this works...
2578 gchar time_buf1[20];
2579 gchar time_buf2[20];
2580 time_buf1[0] = '\0';
2581 time_buf2[0] = '\0';
2582 static gchar tmp_buf[100];
2583 // Compact info: Short date eg (11/20/99), duration and length
2584 // Hopefully these are the things that are most useful and so promoted into the tooltip
2585 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2586 // %x The preferred date representation for the current locale without the time.
2587 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2588 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2589 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2591 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2594 // Get length and consider the appropriate distance units
2595 gdouble tr_len = vik_track_get_length(tr);
2596 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2597 switch (dist_units) {
2598 case VIK_UNITS_DISTANCE_KILOMETRES:
2599 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2601 case VIK_UNITS_DISTANCE_MILES:
2602 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2611 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2613 // Very simple tooltip - may expand detail in the future...
2614 static gchar tmp_buf[32];
2615 g_snprintf (tmp_buf, sizeof(tmp_buf),
2617 g_hash_table_size (l->waypoints));
2621 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2623 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2624 // NB It's OK to return NULL
2629 return w->description;
2638 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2641 * set_statusbar_msg_info_trkpt:
2643 * Function to show track point information on the statusbar
2644 * Items displayed is controlled by the settings format code
2646 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2648 gchar *statusbar_format_code = NULL;
2649 gboolean need2free = FALSE;
2650 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2651 // Otherwise use default
2652 statusbar_format_code = g_strdup ( "KEATDN" );
2656 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2657 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2661 g_free ( statusbar_format_code );
2665 * Function to show basic waypoint information on the statusbar
2667 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2670 switch (a_vik_get_units_height ()) {
2671 case VIK_UNITS_HEIGHT_FEET:
2672 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2675 //VIK_UNITS_HEIGHT_METRES:
2676 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2680 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2681 // one can easily use the current pointer position to see this if needed
2682 gchar *lat = NULL, *lon = NULL;
2683 static struct LatLon ll;
2684 vik_coord_to_latlon (&(wpt->coord), &ll);
2685 a_coords_latlon_to_string ( &ll, &lat, &lon );
2687 // Combine parts to make overall message
2690 // Add comment if available
2691 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2693 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2694 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2701 * General layer selection function, find out which bit is selected and take appropriate action
2703 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2706 l->current_wp = NULL;
2707 l->current_wp_id = NULL;
2708 trw_layer_cancel_current_tp ( l, FALSE );
2711 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2715 case VIK_TREEVIEW_TYPE_LAYER:
2717 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2718 /* Mark for redraw */
2723 case VIK_TREEVIEW_TYPE_SUBLAYER:
2727 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2729 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2730 /* Mark for redraw */
2734 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2736 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2737 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2738 /* Mark for redraw */
2742 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2744 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2745 /* Mark for redraw */
2749 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2751 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2752 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2753 /* Mark for redraw */
2757 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2759 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2760 /* Mark for redraw */
2764 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2766 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2768 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2769 // Show some waypoint info
2770 set_statusbar_msg_info_wpt ( l, wpt );
2771 /* Mark for redraw */
2778 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2787 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2792 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2797 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2802 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2804 return l->waypoints;
2807 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2809 return vtl->tracks_iters;
2812 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2814 return vtl->routes_iters;
2817 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2819 return vtl->waypoints;
2822 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2824 return ! ( g_hash_table_size ( vtl->tracks ) ||
2825 g_hash_table_size ( vtl->routes ) ||
2826 g_hash_table_size ( vtl->waypoints ) );
2829 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2831 return vtl->tracks_visible;
2834 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2836 return vtl->routes_visible;
2839 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2841 return vtl->waypoints_visible;
2845 * ATM use a case sensitive find
2846 * Finds the first one
2848 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2850 if ( wp && wp->name )
2851 if ( ! strcmp ( wp->name, name ) )
2857 * Get waypoint by name - not guaranteed to be unique
2858 * Finds the first one
2860 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2862 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2866 * ATM use a case sensitive find
2867 * Finds the first one
2869 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2871 if ( trk && trk->name )
2872 if ( ! strcmp ( trk->name, name ) )
2878 * Get track by name - not guaranteed to be unique
2879 * Finds the first one
2881 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2883 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2887 * Get route by name - not guaranteed to be unique
2888 * Finds the first one
2890 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2892 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2895 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2897 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2898 maxmin[0].lat = trk->bbox.north;
2899 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2900 maxmin[1].lat = trk->bbox.south;
2901 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2902 maxmin[0].lon = trk->bbox.east;
2903 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2904 maxmin[1].lon = trk->bbox.west;
2907 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2909 // Continually reuse maxmin to find the latest maximum and minimum values
2910 // First set to waypoints bounds
2911 maxmin[0].lat = vtl->waypoints_bbox.north;
2912 maxmin[1].lat = vtl->waypoints_bbox.south;
2913 maxmin[0].lon = vtl->waypoints_bbox.east;
2914 maxmin[1].lon = vtl->waypoints_bbox.west;
2915 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2916 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2919 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2921 /* 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... */
2922 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2923 trw_layer_find_maxmin (vtl, maxmin);
2924 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2928 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2929 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2934 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2937 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2938 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2940 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2943 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2945 /* First set the center [in case previously viewing from elsewhere] */
2946 /* Then loop through zoom levels until provided positions are in view */
2947 /* This method is not particularly fast - but should work well enough */
2948 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2950 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2951 vik_viewport_set_center_coord ( vvp, &coord );
2953 /* Convert into definite 'smallest' and 'largest' positions */
2954 struct LatLon minmin;
2955 if ( maxmin[0].lat < maxmin[1].lat )
2956 minmin.lat = maxmin[0].lat;
2958 minmin.lat = maxmin[1].lat;
2960 struct LatLon maxmax;
2961 if ( maxmin[0].lon > maxmin[1].lon )
2962 maxmax.lon = maxmin[0].lon;
2964 maxmax.lon = maxmin[1].lon;
2966 /* Never zoom in too far - generally not that useful, as too close ! */
2967 /* Always recalculate the 'best' zoom level */
2969 vik_viewport_set_zoom ( vvp, zoom );
2971 gdouble min_lat, max_lat, min_lon, max_lon;
2972 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2973 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2974 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2975 /* NB I think the logic used in this test to determine if the bounds is within view
2976 fails if track goes across 180 degrees longitude.
2977 Hopefully that situation is not too common...
2978 Mind you viking doesn't really do edge locations to well anyway */
2979 if ( min_lat < minmin.lat &&
2980 max_lat > minmin.lat &&
2981 min_lon < maxmax.lon &&
2982 max_lon > maxmax.lon )
2983 /* Found within zoom level */
2988 vik_viewport_set_zoom ( vvp, zoom );
2992 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2994 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2995 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2996 trw_layer_find_maxmin (vtl, maxmin);
2997 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3000 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3005 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
3007 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])) ) ) {
3008 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3011 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3014 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3016 GtkWidget *file_selector;
3018 gboolean failed = FALSE;
3019 file_selector = gtk_file_chooser_dialog_new (title,
3021 GTK_FILE_CHOOSER_ACTION_SAVE,
3022 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3023 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3025 gchar *cwd = g_get_current_dir();
3027 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3031 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3033 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3035 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3036 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ||
3037 a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3039 gtk_widget_hide ( file_selector );
3040 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3041 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3042 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3046 gtk_widget_destroy ( file_selector );
3048 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3051 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3053 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3056 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3058 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3061 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3063 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3064 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3065 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3066 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3068 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3070 g_free ( auto_save_name );
3073 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3075 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3076 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3077 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3078 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3080 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3082 g_free ( auto_save_name );
3086 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3089 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3091 gchar *name_used = NULL;
3094 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3095 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3096 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3097 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3099 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3103 gchar *quoted_file = g_shell_quote ( name_used );
3104 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3105 g_free ( quoted_file );
3106 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3108 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3109 g_error_free ( err );
3113 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3114 //g_remove ( name_used );
3115 // Perhaps should be deleted when the program ends?
3116 // For now leave it to the user to delete it / use system temp cleanup methods.
3117 g_free ( name_used );
3121 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3123 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3126 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3128 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3131 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3133 gpointer layer_and_vlp[2];
3134 layer_and_vlp[0] = pass_along[0];
3135 layer_and_vlp[1] = pass_along[1];
3137 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3139 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3140 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3142 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3144 if ( !trk || !trk->name )
3147 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3148 gchar *auto_save_name = g_strdup ( trk->name );
3149 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3150 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3152 gchar *label = NULL;
3153 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3154 label = _("Export Route as GPX");
3156 label = _("Export Track as GPX");
3157 trw_layer_export ( layer_and_vlp, label, auto_save_name, trk, FILE_TYPE_GPX );
3159 g_free ( auto_save_name );
3162 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3164 wpu_udata *user_data = udata;
3165 if ( wp == user_data->wp ) {
3166 user_data->uuid = id;
3172 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3174 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3175 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3176 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3178 GTK_RESPONSE_REJECT,
3180 GTK_RESPONSE_ACCEPT,
3183 GtkWidget *label, *entry;
3184 label = gtk_label_new(_("Waypoint Name:"));
3185 entry = gtk_entry_new();
3187 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3188 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3189 gtk_widget_show_all ( label );
3190 gtk_widget_show_all ( entry );
3192 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3194 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3196 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3197 // Find *first* wp with the given name
3198 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3201 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3204 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3205 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3207 // Find and select on the side panel
3212 // Hmmm, want key of it
3213 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3215 if ( wpf && udata.uuid ) {
3216 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3217 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3226 gtk_widget_destroy ( dia );
3229 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3231 gchar *default_name = highest_wp_number_get(vtl);
3232 VikWaypoint *wp = vik_waypoint_new();
3233 gchar *returned_name;
3235 wp->coord = *def_coord;
3237 // Attempt to auto set height if DEM data is available
3238 vik_waypoint_apply_dem_data ( wp, TRUE );
3240 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3242 if ( returned_name )
3245 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3246 g_free (default_name);
3247 g_free (returned_name);
3250 g_free (default_name);
3251 vik_waypoint_free(wp);
3255 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3257 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3258 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3259 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3260 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3261 VikViewport *vvp = vik_window_viewport(vw);
3263 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3264 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3265 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3266 trw_layer_calculate_bounds_waypoints ( vtl );
3267 vik_layers_panel_emit_update ( vlp );
3270 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3272 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3273 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3274 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3276 trw_layer_find_maxmin (vtl, maxmin);
3277 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3278 trw_layer_calculate_bounds_waypoints ( vtl );
3279 vik_layers_panel_emit_update ( vlp );
3282 #ifdef VIK_CONFIG_GEOTAG
3283 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3285 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3287 // Update directly - not changing the mtime
3288 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3291 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3293 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3296 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3300 * Use code in separate file for this feature as reasonably complex
3302 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3304 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3305 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3306 // Unset so can be reverified later if necessary
3307 vtl->has_verified_thumbnails = FALSE;
3309 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3315 static void trw_layer_geotagging_waypoint ( gpointer pass_along[6] )
3317 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3318 VikWaypoint *wpt = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3320 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3326 static void trw_layer_geotagging ( gpointer lav[2] )
3328 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3329 // Unset so can be reverified later if necessary
3330 vtl->has_verified_thumbnails = FALSE;
3332 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3339 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3342 * Acquire into this TRW Layer straight from GPS Device
3344 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3346 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3347 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3348 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3349 VikViewport *vvp = vik_window_viewport(vw);
3351 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3352 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3356 * Acquire into this TRW Layer from Directions
3358 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3360 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3361 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3362 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3363 VikViewport *vvp = vik_window_viewport(vw);
3365 a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3369 * Acquire into this TRW Layer from an entered URL
3371 static void trw_layer_acquire_url_cb ( gpointer lav[2] )
3373 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3374 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3375 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3376 VikViewport *vvp = vik_window_viewport(vw);
3378 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3379 a_acquire ( vw, vlp, vvp, &vik_datasource_url_interface, NULL, NULL );
3382 #ifdef VIK_CONFIG_OPENSTREETMAP
3384 * Acquire into this TRW Layer from OSM
3386 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3388 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3389 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3390 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3391 VikViewport *vvp = vik_window_viewport(vw);
3393 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3397 * Acquire into this TRW Layer from OSM for 'My' Traces
3399 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3401 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3402 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3403 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3404 VikViewport *vvp = vik_window_viewport(vw);
3406 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3410 #ifdef VIK_CONFIG_GEOCACHES
3412 * Acquire into this TRW Layer from Geocaching.com
3414 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3416 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3417 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3418 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3419 VikViewport *vvp = vik_window_viewport(vw);
3421 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3425 #ifdef VIK_CONFIG_GEOTAG
3427 * Acquire into this TRW Layer from images
3429 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3431 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3432 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3433 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3434 VikViewport *vvp = vik_window_viewport(vw);
3436 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3437 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3439 // Reverify thumbnails as they may have changed
3440 vtl->has_verified_thumbnails = FALSE;
3441 trw_layer_verify_thumbnails ( vtl, NULL );
3445 static void trw_layer_gps_upload ( gpointer lav[2] )
3447 gpointer pass_along[6];
3448 pass_along[0] = lav[0];
3449 pass_along[1] = lav[1];
3450 pass_along[2] = NULL; // No track - operate on the layer
3451 pass_along[3] = NULL;
3452 pass_along[4] = NULL;
3453 pass_along[5] = NULL;
3455 trw_layer_gps_upload_any ( pass_along );
3459 * If pass_along[3] is defined that this will upload just that track
3461 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3463 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3464 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3466 // May not actually get a track here as pass_along[2&3] can be null
3467 VikTrack *track = NULL;
3468 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3469 gboolean xfer_all = FALSE;
3471 if ( pass_along[2] ) {
3473 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3474 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3477 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3478 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3481 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3484 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3488 else if ( !pass_along[4] )
3489 xfer_all = TRUE; // i.e. whole layer
3491 if (track && !track->visible) {
3492 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3496 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3497 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3498 GTK_DIALOG_DESTROY_WITH_PARENT,
3500 GTK_RESPONSE_ACCEPT,
3502 GTK_RESPONSE_REJECT,
3505 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3506 GtkWidget *response_w = NULL;
3507 #if GTK_CHECK_VERSION (2, 20, 0)
3508 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3512 gtk_widget_grab_focus ( response_w );
3514 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3516 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3517 datasource_gps_clean_up ( dgs );
3518 gtk_widget_destroy ( dialog );
3522 // Get info from reused datasource dialog widgets
3523 gchar* protocol = datasource_gps_get_protocol ( dgs );
3524 gchar* port = datasource_gps_get_descriptor ( dgs );
3525 // NB don't free the above strings as they're references to values held elsewhere
3526 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3527 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3528 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3529 gboolean turn_off = datasource_gps_get_off ( dgs );
3531 gtk_widget_destroy ( dialog );
3533 // When called from the viewport - work the corresponding layerspanel:
3535 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3538 // Apply settings to transfer to the GPS device
3545 vik_layers_panel_get_viewport (vlp),
3554 * Acquire into this TRW Layer from any GPS Babel supported file
3556 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3558 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3559 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3560 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3561 VikViewport *vvp = vik_window_viewport(vw);
3563 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3566 static void trw_layer_new_wp ( gpointer lav[2] )
3568 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3569 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3570 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3571 instead return true if you want to update. */
3572 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 ) {
3573 trw_layer_calculate_bounds_waypoints ( vtl );
3574 vik_layers_panel_emit_update ( vlp );
3578 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3580 vtl->current_track = vik_track_new();
3581 vik_track_set_defaults ( vtl->current_track );
3582 vtl->current_track->visible = TRUE;
3583 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3584 // Create track with the preferred colour from the layer properties
3585 vtl->current_track->color = vtl->track_color;
3587 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3588 vtl->current_track->has_color = TRUE;
3589 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3592 static void trw_layer_new_track ( gpointer lav[2] )
3594 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3596 if ( ! vtl->current_track ) {
3597 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3598 new_track_create_common ( vtl, name );
3601 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3605 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3607 vtl->current_track = vik_track_new();
3608 vik_track_set_defaults ( vtl->current_track );
3609 vtl->current_track->visible = TRUE;
3610 vtl->current_track->is_route = TRUE;
3611 // By default make all routes red
3612 vtl->current_track->has_color = TRUE;
3613 gdk_color_parse ( "red", &vtl->current_track->color );
3614 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3617 static void trw_layer_new_route ( gpointer lav[2] )
3619 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3621 if ( ! vtl->current_track ) {
3622 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3623 new_route_create_common ( vtl, name );
3625 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3629 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3631 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3632 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3634 if ( g_hash_table_size (vtl->routes) > 0 ) {
3635 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3636 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3637 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3638 vik_layers_panel_emit_update ( vlp );
3643 static void trw_layer_finish_track ( gpointer lav[2] )
3645 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3646 vtl->current_track = NULL;
3647 vik_layer_emit_update ( VIK_LAYER(vtl) );
3650 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3652 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3653 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3655 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3656 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3657 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3658 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3659 vik_layers_panel_emit_update ( vlp );
3663 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3665 /* NB do not care if wp is visible or not */
3666 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3669 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3671 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3672 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3674 /* Only 1 waypoint - jump straight to it */
3675 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3676 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3677 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3679 /* If at least 2 waypoints - find center and then zoom to fit */
3680 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3682 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3683 maxmin[0].lat = vtl->waypoints_bbox.north;
3684 maxmin[1].lat = vtl->waypoints_bbox.south;
3685 maxmin[0].lon = vtl->waypoints_bbox.east;
3686 maxmin[1].lon = vtl->waypoints_bbox.west;
3687 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3690 vik_layers_panel_emit_update ( vlp );
3693 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3695 static gpointer pass_along[2];
3697 GtkWidget *export_submenu;
3698 pass_along[0] = vtl;
3699 pass_along[1] = vlp;
3701 item = gtk_menu_item_new();
3702 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3703 gtk_widget_show ( item );
3705 if ( vtl->current_track ) {
3706 if ( vtl->current_track->is_route )
3707 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3709 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3711 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3712 gtk_widget_show ( item );
3715 item = gtk_menu_item_new ();
3716 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3717 gtk_widget_show ( item );
3720 /* Now with icons */
3721 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3722 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3723 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3724 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3725 gtk_widget_show ( item );
3727 GtkWidget *view_submenu = gtk_menu_new();
3728 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3729 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3730 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3731 gtk_widget_show ( item );
3732 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3734 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3736 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3737 gtk_widget_show ( item );
3739 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3741 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3742 gtk_widget_show ( item );
3744 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3746 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3747 gtk_widget_show ( item );
3749 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3750 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3751 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3752 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3753 gtk_widget_show ( item );
3755 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3756 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3757 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3758 gtk_widget_show ( item );
3760 export_submenu = gtk_menu_new ();
3761 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3763 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3764 gtk_widget_show ( item );
3765 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3767 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3768 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3769 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3770 gtk_widget_show ( item );
3772 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3773 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3774 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3775 gtk_widget_show ( item );
3777 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3778 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3779 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3780 gtk_widget_show ( item );
3782 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3784 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3785 gtk_widget_show ( item );
3787 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3788 item = gtk_menu_item_new_with_mnemonic ( external1 );
3789 g_free ( external1 );
3790 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3791 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3792 gtk_widget_show ( item );
3794 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3795 item = gtk_menu_item_new_with_mnemonic ( external2 );
3796 g_free ( external2 );
3797 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3798 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3799 gtk_widget_show ( item );
3801 GtkWidget *new_submenu = gtk_menu_new();
3802 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3803 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, 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), new_submenu);
3808 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3809 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3810 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3811 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3812 gtk_widget_show ( item );
3814 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3817 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3818 gtk_widget_show ( item );
3819 // Make it available only when a new track *not* already in progress
3820 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3822 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3825 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3826 gtk_widget_show ( item );
3827 // Make it available only when a new track *not* already in progress
3828 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3830 #ifdef VIK_CONFIG_GEOTAG
3831 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3832 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3833 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3834 gtk_widget_show ( item );
3837 GtkWidget *acquire_submenu = gtk_menu_new ();
3838 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3839 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3840 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3841 gtk_widget_show ( item );
3842 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3844 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3846 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3847 gtk_widget_show ( item );
3849 /* FIXME: only add menu when at least a routing engine has support for Directions */
3850 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3851 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3852 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3853 gtk_widget_show ( item );
3855 #ifdef VIK_CONFIG_OPENSTREETMAP
3856 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3857 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3858 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3859 gtk_widget_show ( item );
3861 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3863 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3864 gtk_widget_show ( item );
3867 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
3868 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
3869 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3870 gtk_widget_show ( item );
3872 #ifdef VIK_CONFIG_GEONAMES
3873 GtkWidget *wikipedia_submenu = gtk_menu_new();
3874 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3876 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3877 gtk_widget_show(item);
3878 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3880 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3883 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3884 gtk_widget_show ( item );
3886 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3887 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3889 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3890 gtk_widget_show ( item );
3893 #ifdef VIK_CONFIG_GEOCACHES
3894 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3896 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3897 gtk_widget_show ( item );
3900 #ifdef VIK_CONFIG_GEOTAG
3901 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3903 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3904 gtk_widget_show ( item );
3907 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3909 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3910 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
3911 gtk_widget_show ( item );
3913 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3915 GtkWidget *upload_submenu = gtk_menu_new ();
3916 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3917 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3918 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3919 gtk_widget_show ( item );
3920 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3922 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3923 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3925 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3926 gtk_widget_show ( item );
3928 #ifdef VIK_CONFIG_OPENSTREETMAP
3929 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3930 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3931 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3932 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3933 gtk_widget_show ( item );
3936 GtkWidget *delete_submenu = gtk_menu_new ();
3937 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3939 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3940 gtk_widget_show ( item );
3941 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3943 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3944 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3946 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3947 gtk_widget_show ( item );
3949 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3950 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3951 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3952 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3953 gtk_widget_show ( item );
3955 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3956 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3958 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3959 gtk_widget_show ( item );
3961 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3962 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3964 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3965 gtk_widget_show ( item );
3967 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3968 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3970 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3971 gtk_widget_show ( item );
3973 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3974 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3975 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3976 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3977 gtk_widget_show ( item );
3979 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3980 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3982 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3983 gtk_widget_show ( item );
3986 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3987 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3989 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3990 gtk_widget_show ( item );
3993 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
3994 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
3996 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3997 gtk_widget_show ( item );
3998 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4000 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4001 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4003 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4004 gtk_widget_show ( item );
4005 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4008 // Fake Waypoint UUIDs vi simple increasing integer
4009 static guint wp_uuid = 0;
4011 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4015 vik_waypoint_set_name (wp, name);
4017 if ( VIK_LAYER(vtl)->realized )
4019 // Do we need to create the sublayer:
4020 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4021 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4024 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4026 // Visibility column always needed for waypoints
4027 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 );
4029 // Actual setting of visibility dependent on the waypoint
4030 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4032 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4034 // Sort now as post_read is not called on a realized waypoint
4035 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4038 highest_wp_number_add_wp(vtl, name);
4039 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4043 // Fake Track UUIDs vi simple increasing integer
4044 static guint tr_uuid = 0;
4046 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4050 vik_track_set_name (t, name);
4052 if ( VIK_LAYER(vtl)->realized )
4054 // Do we need to create the sublayer:
4055 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4056 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4059 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4060 // Visibility column always needed for tracks
4061 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 );
4063 // Actual setting of visibility dependent on the track
4064 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4066 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4068 // Sort now as post_read is not called on a realized track
4069 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4072 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4074 trw_layer_update_treeview ( vtl, t );
4077 // Fake Route UUIDs vi simple increasing integer
4078 static guint rt_uuid = 0;
4080 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4084 vik_track_set_name (t, name);
4086 if ( VIK_LAYER(vtl)->realized )
4088 // Do we need to create the sublayer:
4089 if ( g_hash_table_size (vtl->routes) == 0 ) {
4090 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4093 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4094 // Visibility column always needed for routes
4095 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 );
4096 // Actual setting of visibility dependent on the route
4097 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4099 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4101 // Sort now as post_read is not called on a realized route
4102 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4105 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4107 trw_layer_update_treeview ( vtl, t );
4110 /* to be called whenever a track has been deleted or may have been changed. */
4111 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4113 if (vtl->current_tp_track == trk )
4114 trw_layer_cancel_current_tp ( vtl, FALSE );
4118 * Normally this is done to due the waypoint size preference having changed
4120 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4122 GHashTableIter iter;
4123 gpointer key, value;
4126 g_hash_table_iter_init ( &iter, vtl->waypoints );
4127 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4128 VikWaypoint *wp = VIK_WAYPOINT(value);
4130 // Reapply symbol setting to update the pixbuf
4131 gchar *tmp_symbol = g_strdup ( wp->symbol );
4132 vik_waypoint_set_symbol ( wp, tmp_symbol );
4133 g_free ( tmp_symbol );
4139 * trw_layer_new_unique_sublayer_name:
4141 * Allocates a unique new name
4143 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4146 gchar *newname = g_strdup(name);
4151 switch ( sublayer_type ) {
4152 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4153 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4155 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4156 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4159 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4162 // If found a name already in use try adding 1 to it and we try again
4164 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4166 newname = new_newname;
4169 } while ( id != NULL);
4174 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4176 // No more uniqueness of name forced when loading from a file
4177 // This now makes this function a little redunant as we just flow the parameters through
4178 vik_trw_layer_add_waypoint ( vtl, name, wp );
4181 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4183 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4184 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4185 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4186 vik_track_free ( tr );
4187 vtl->route_finder_append = FALSE; /* this means we have added it */
4190 // No more uniqueness of name forced when loading from a file
4192 vik_trw_layer_add_route ( vtl, name, tr );
4194 vik_trw_layer_add_track ( vtl, name, tr );
4196 if ( vtl->route_finder_check_added_track ) {
4197 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4198 vtl->route_finder_added_track = tr;
4203 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4205 *l = g_list_append(*l, id);
4209 * Move an item from one TRW layer to another TRW layer
4211 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4213 // TODO reconsider strategy when moving within layer (if anything...)
4214 gboolean rename = ( vtl_src != vtl_dest );
4218 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4219 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4223 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4225 newname = g_strdup ( trk->name );
4227 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4228 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4230 vik_trw_layer_delete_track ( vtl_src, trk );
4233 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4234 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4238 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4240 newname = g_strdup ( trk->name );
4242 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4243 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4245 vik_trw_layer_delete_route ( vtl_src, trk );
4248 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4249 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4253 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4255 newname = g_strdup ( wp->name );
4257 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4258 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4260 trw_layer_delete_waypoint ( vtl_src, wp );
4262 // Recalculate bounds even if not renamed as maybe dragged between layers
4263 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4264 trw_layer_calculate_bounds_waypoints ( vtl_src );
4268 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4270 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4271 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4273 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4274 GList *items = NULL;
4277 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4278 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4280 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4281 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4283 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4284 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4289 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4290 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4291 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4292 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4294 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4301 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4302 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4306 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4308 trku_udata *user_data = udata;
4309 if ( trk == user_data->trk ) {
4310 user_data->uuid = id;
4316 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4318 gboolean was_visible = FALSE;
4320 if ( trk && trk->name ) {
4322 if ( trk == vtl->current_track ) {
4323 vtl->current_track = NULL;
4324 vtl->current_tp_track = NULL;
4325 vtl->current_tp_id = NULL;
4326 vtl->moving_tp = FALSE;
4329 was_visible = trk->visible;
4331 if ( trk == vtl->route_finder_current_track )
4332 vtl->route_finder_current_track = NULL;
4334 if ( trk == vtl->route_finder_added_track )
4335 vtl->route_finder_added_track = NULL;
4341 // Hmmm, want key of it
4342 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4344 if ( trkf && udata.uuid ) {
4345 /* could be current_tp, so we have to check */
4346 trw_layer_cancel_tps_of_track ( vtl, trk );
4348 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4351 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4352 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4353 g_hash_table_remove ( vtl->tracks, udata.uuid );
4355 // If last sublayer, then remove sublayer container
4356 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4357 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4365 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4367 gboolean was_visible = FALSE;
4369 if ( trk && trk->name ) {
4371 if ( trk == vtl->current_track ) {
4372 vtl->current_track = NULL;
4373 vtl->current_tp_track = NULL;
4374 vtl->current_tp_id = NULL;
4375 vtl->moving_tp = FALSE;
4378 was_visible = trk->visible;
4380 if ( trk == vtl->route_finder_current_track )
4381 vtl->route_finder_current_track = NULL;
4383 if ( trk == vtl->route_finder_added_track )
4384 vtl->route_finder_added_track = NULL;
4390 // Hmmm, want key of it
4391 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4393 if ( trkf && udata.uuid ) {
4394 /* could be current_tp, so we have to check */
4395 trw_layer_cancel_tps_of_track ( vtl, trk );
4397 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4400 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4401 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4402 g_hash_table_remove ( vtl->routes, udata.uuid );
4404 // If last sublayer, then remove sublayer container
4405 if ( g_hash_table_size (vtl->routes) == 0 ) {
4406 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4414 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4416 gboolean was_visible = FALSE;
4418 if ( wp && wp->name ) {
4420 if ( wp == vtl->current_wp ) {
4421 vtl->current_wp = NULL;
4422 vtl->current_wp_id = NULL;
4423 vtl->moving_wp = FALSE;
4426 was_visible = wp->visible;
4432 // Hmmm, want key of it
4433 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4435 if ( wpf && udata.uuid ) {
4436 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4439 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4440 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4442 highest_wp_number_remove_wp(vtl, wp->name);
4443 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4445 // If last sublayer, then remove sublayer container
4446 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4447 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4457 // Only for temporary use by trw_layer_delete_waypoint_by_name
4458 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4460 wpu_udata *user_data = udata;
4461 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4462 user_data->uuid = id;
4469 * Delete a waypoint by the given name
4470 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4471 * as there be multiple waypoints with the same name
4473 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4476 // Fake a waypoint with the given name
4477 udata.wp = vik_waypoint_new ();
4478 vik_waypoint_set_name (udata.wp, name);
4479 // Currently only the name is used in this waypoint find function
4482 // Hmmm, want key of it
4483 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4485 vik_waypoint_free (udata.wp);
4487 if ( wpf && udata.uuid )
4488 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4494 VikTrack *trk; // input
4495 gpointer uuid; // output
4498 // Only for temporary use by trw_layer_delete_track_by_name
4499 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4501 tpu_udata *user_data = udata;
4502 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4503 user_data->uuid = id;
4510 * Delete a track by the given name
4511 * NOTE: ATM this will delete the first encountered Track with the specified name
4512 * as there may be multiple tracks with the same name within the specified hash table
4514 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4517 // Fake a track with the given name
4518 udata.trk = vik_track_new ();
4519 vik_track_set_name (udata.trk, name);
4520 // Currently only the name is used in this waypoint find function
4523 // Hmmm, want key of it
4524 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4526 vik_track_free (udata.trk);
4528 if ( trkf && udata.uuid ) {
4529 // This could be a little better written...
4530 if ( vtl->tracks == ht_tracks )
4531 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4532 if ( vtl->routes == ht_tracks )
4533 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4540 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4542 vik_treeview_item_delete (vt, it );
4545 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4548 vtl->current_track = NULL;
4549 vtl->route_finder_current_track = NULL;
4550 vtl->route_finder_added_track = NULL;
4551 if (vtl->current_tp_track)
4552 trw_layer_cancel_current_tp(vtl, FALSE);
4554 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4555 g_hash_table_remove_all(vtl->routes_iters);
4556 g_hash_table_remove_all(vtl->routes);
4558 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4560 vik_layer_emit_update ( VIK_LAYER(vtl) );
4563 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4566 vtl->current_track = NULL;
4567 vtl->route_finder_current_track = NULL;
4568 vtl->route_finder_added_track = NULL;
4569 if (vtl->current_tp_track)
4570 trw_layer_cancel_current_tp(vtl, FALSE);
4572 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4573 g_hash_table_remove_all(vtl->tracks_iters);
4574 g_hash_table_remove_all(vtl->tracks);
4576 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4578 vik_layer_emit_update ( VIK_LAYER(vtl) );
4581 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4583 vtl->current_wp = NULL;
4584 vtl->current_wp_id = NULL;
4585 vtl->moving_wp = FALSE;
4587 highest_wp_number_reset(vtl);
4589 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4590 g_hash_table_remove_all(vtl->waypoints_iters);
4591 g_hash_table_remove_all(vtl->waypoints);
4593 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4595 vik_layer_emit_update ( VIK_LAYER(vtl) );
4598 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4600 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4601 // Get confirmation from the user
4602 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4603 _("Are you sure you want to delete all tracks in %s?"),
4604 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4605 vik_trw_layer_delete_all_tracks (vtl);
4608 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4610 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4611 // Get confirmation from the user
4612 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4613 _("Are you sure you want to delete all routes in %s?"),
4614 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4615 vik_trw_layer_delete_all_routes (vtl);
4618 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4620 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4621 // Get confirmation from the user
4622 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4623 _("Are you sure you want to delete all waypoints in %s?"),
4624 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4625 vik_trw_layer_delete_all_waypoints (vtl);
4628 static void trw_layer_delete_item ( gpointer pass_along[6] )
4630 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4631 gboolean was_visible = FALSE;
4632 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4634 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4635 if ( wp && wp->name ) {
4636 if ( GPOINTER_TO_INT ( pass_along[4]) )
4637 // Get confirmation from the user
4638 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4639 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4640 _("Are you sure you want to delete the waypoint \"%s\"?"),
4643 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4646 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4648 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4649 if ( trk && trk->name ) {
4650 if ( GPOINTER_TO_INT ( pass_along[4]) )
4651 // Get confirmation from the user
4652 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4653 _("Are you sure you want to delete the track \"%s\"?"),
4656 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4661 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4662 if ( trk && trk->name ) {
4663 if ( GPOINTER_TO_INT ( pass_along[4]) )
4664 // Get confirmation from the user
4665 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4666 _("Are you sure you want to delete the route \"%s\"?"),
4669 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4673 vik_layer_emit_update ( VIK_LAYER(vtl) );
4677 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4679 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4681 vik_waypoint_set_name ( wp, new_name );
4683 // Now update the treeview as well
4688 // Need key of it for treeview update
4689 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4691 if ( wpf && udataU.uuid ) {
4692 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4695 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4696 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4702 * Maintain icon of waypoint in the treeview
4704 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4706 // update the treeview
4711 // Need key of it for treeview update
4712 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4714 if ( wpf && udataU.uuid ) {
4715 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4718 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4723 static void trw_layer_properties_item ( gpointer pass_along[7] )
4725 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4726 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4728 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4730 if ( wp && wp->name )
4732 gboolean updated = FALSE;
4733 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4735 trw_layer_waypoint_rename ( vtl, wp, new_name );
4737 if ( updated && pass_along[6] )
4738 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4740 if ( updated && VIK_LAYER(vtl)->visible )
4741 vik_layer_emit_update ( VIK_LAYER(vtl) );
4747 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4748 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4750 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4752 if ( tr && tr->name )
4754 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4757 pass_along[1], /* vlp */
4758 pass_along[5], /* vvp */
4765 * trw_layer_track_statistics:
4767 * Show track statistics.
4768 * ATM jump to the stats page in the properties
4769 * TODO: consider separating the stats into an individual dialog?
4771 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4773 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4775 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4776 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4778 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4780 if ( trk && trk->name ) {
4781 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4784 pass_along[1], // vlp
4785 pass_along[5], // vvp
4791 * Update the treeview of the track id - primarily to update the icon
4793 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4799 gpointer *trkf = NULL;
4800 if ( trk->is_route )
4801 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4803 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4805 if ( trkf && udata.uuid ) {
4807 GtkTreeIter *iter = NULL;
4808 if ( trk->is_route )
4809 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4811 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4814 // TODO: Make this a function
4815 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4816 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4817 ((trk->color.green & 0xff00) << 8) |
4818 (trk->color.blue & 0xff00);
4819 gdk_pixbuf_fill ( pixbuf, pixel );
4820 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4821 g_object_unref (pixbuf);
4828 Parameter 1 -> VikLayersPanel
4829 Parameter 2 -> VikLayer
4830 Parameter 3 -> VikViewport
4832 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4835 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4836 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4839 /* since vlp not set, vl & vvp should be valid instead! */
4841 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4842 vik_layer_emit_update ( VIK_LAYER(vl) );
4847 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4849 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4851 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4852 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4854 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4856 if ( track && track->trackpoints )
4857 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4860 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4862 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4864 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4865 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4867 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4869 if ( track && track->trackpoints )
4871 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4873 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4874 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4875 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4876 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4877 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4881 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4883 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4885 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4886 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4888 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4893 // Converting a track to a route can be a bit more complicated,
4894 // so give a chance to change our minds:
4895 if ( !trk->is_route &&
4896 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4897 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4899 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4900 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4905 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4908 trk_copy->is_route = !trk_copy->is_route;
4910 // ATM can't set name to self - so must create temporary copy
4911 gchar *name = g_strdup ( trk_copy->name );
4913 // Delete old one and then add new one
4914 if ( trk->is_route ) {
4915 vik_trw_layer_delete_route ( vtl, trk );
4916 vik_trw_layer_add_track ( vtl, name, trk_copy );
4919 // Extra route conversion bits...
4920 vik_track_merge_segments ( trk_copy );
4921 vik_track_to_routepoints ( trk_copy );
4923 vik_trw_layer_delete_track ( vtl, trk );
4924 vik_trw_layer_add_route ( vtl, name, trk_copy );
4928 // Update in case color of track / route changes when moving between sublayers
4929 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4932 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4934 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4936 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4937 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4939 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4942 vik_track_anonymize_times ( track );
4945 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4947 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4949 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4950 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4952 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4957 vtl->current_track = track;
4958 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);
4960 if ( track->trackpoints )
4961 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4965 * extend a track using route finder
4967 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4969 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4970 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4973 if ( !track->trackpoints )
4975 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4977 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4978 vtl->route_finder_coord = last_coord;
4979 vtl->route_finder_current_track = track;
4980 vtl->route_finder_started = TRUE;
4982 if ( track->trackpoints )
4983 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4990 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4992 // If have a vlp then perform a basic test to see if any DEM info available...
4994 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4996 if ( !g_list_length(dems) ) {
4997 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5005 * apply_dem_data_common:
5007 * A common function for applying the DEM values and reporting the results.
5009 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5011 if ( !trw_layer_dem_test ( vtl, vlp ) )
5014 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5015 // Inform user how much was changed
5017 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5018 g_snprintf(str, 64, tmp_str, changed);
5019 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5022 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
5024 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5026 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5027 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5029 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5032 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
5035 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
5037 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5039 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5040 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5042 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5045 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
5051 * A common function for applying the elevation smoothing and reporting the results.
5053 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5055 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5056 // Inform user how much was changed
5058 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5059 g_snprintf(str, 64, tmp_str, changed);
5060 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5066 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5068 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5070 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5071 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5073 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5078 smooth_it ( vtl, track, FALSE );
5081 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5083 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5085 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5086 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5088 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5093 smooth_it ( vtl, track, TRUE );
5097 * Commonal helper function
5099 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5102 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5103 g_snprintf(str, 64, tmp_str, changed);
5104 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5107 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5109 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5110 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5112 if ( !trw_layer_dem_test ( vtl, vlp ) )
5116 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5118 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5120 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5124 GHashTableIter iter;
5125 gpointer key, value;
5127 g_hash_table_iter_init ( &iter, vtl->waypoints );
5128 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5129 VikWaypoint *wp = VIK_WAYPOINT(value);
5130 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5133 wp_changed_message ( vtl, changed );
5136 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5138 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5139 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5141 if ( !trw_layer_dem_test ( vtl, vlp ) )
5145 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5147 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5149 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5153 GHashTableIter iter;
5154 gpointer key, value;
5156 g_hash_table_iter_init ( &iter, vtl->waypoints );
5157 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5158 VikWaypoint *wp = VIK_WAYPOINT(value);
5159 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5162 wp_changed_message ( vtl, changed );
5165 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5167 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5169 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5170 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5172 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5177 GList *trps = track->trackpoints;
5180 trps = g_list_last(trps);
5181 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5184 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5186 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5188 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5189 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5191 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5196 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5199 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5202 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5204 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5206 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5207 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5209 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5214 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5217 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5220 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5222 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5224 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5225 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5227 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5232 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5235 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5239 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5241 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5243 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5245 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5246 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5248 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5250 if ( trk && trk->trackpoints )
5252 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5253 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5254 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5255 if ( pass_along[1] )
5256 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5258 vik_layer_emit_update ( VIK_LAYER(vtl) );
5263 * Refine the selected track/route with a routing engine.
5264 * The routing engine is selected by the user, when requestiong the job.
5266 static void trw_layer_route_refine ( gpointer pass_along[6] )
5268 static gint last_engine = 0;
5269 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5272 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5273 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5275 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5277 if ( trk && trk->trackpoints )
5279 /* Check size of the route */
5280 int nb = vik_track_get_tp_count(trk);
5282 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5283 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5284 GTK_MESSAGE_WARNING,
5285 GTK_BUTTONS_OK_CANCEL,
5286 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5288 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5289 gtk_widget_destroy ( dialog );
5290 if (response != GTK_RESPONSE_OK )
5293 /* Select engine from dialog */
5294 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5295 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5296 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5298 GTK_RESPONSE_REJECT,
5300 GTK_RESPONSE_ACCEPT,
5302 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5303 gtk_widget_show_all(label);
5305 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5307 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5308 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5309 gtk_widget_show_all(combo);
5311 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5313 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5315 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5317 /* Dialog validated: retrieve selected engine and do the job */
5318 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5319 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5322 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5324 /* Force saving track */
5325 /* FIXME: remove or rename this hack */
5326 vtl->route_finder_check_added_track = TRUE;
5329 vik_routing_engine_refine (routing, vtl, trk);
5331 /* FIXME: remove or rename this hack */
5332 if ( vtl->route_finder_added_track )
5333 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5335 vtl->route_finder_added_track = NULL;
5336 vtl->route_finder_check_added_track = FALSE;
5338 vik_layer_emit_update ( VIK_LAYER(vtl) );
5340 /* Restore cursor */
5341 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5343 gtk_widget_destroy ( dialog );
5347 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5349 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5350 trw_layer_tpwin_init ( vtl );
5353 /*************************************
5354 * merge/split by time routines
5355 *************************************/
5357 /* called for each key in track hash table.
5358 * If the current track has the same time stamp type, add it to the result,
5359 * except the one pointed by "exclude".
5360 * set exclude to NULL if there is no exclude to check.
5361 * Note that the result is in reverse (for performance reasons).
5366 gboolean with_timestamps;
5368 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5370 twt_udata *user_data = udata;
5371 VikTrackpoint *p1, *p2;
5373 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5377 if (VIK_TRACK(value)->trackpoints) {
5378 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5379 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5381 if ( user_data->with_timestamps ) {
5382 if (!p1->has_timestamp || !p2->has_timestamp) {
5387 // Don't add tracks with timestamps when getting non timestamp tracks
5388 if (p1->has_timestamp || p2->has_timestamp) {
5394 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5397 /* called for each key in track hash table. if original track user_data[1] is close enough
5398 * to the passed one, add it to list in user_data[0]
5400 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5403 VikTrackpoint *p1, *p2;
5404 VikTrack *trk = VIK_TRACK(value);
5406 GList **nearby_tracks = ((gpointer *)user_data)[0];
5407 GList *tpoints = ((gpointer *)user_data)[1];
5410 * detect reasons for not merging, and return
5411 * if no reason is found not to merge, then do it.
5414 // Exclude the original track from the compiled list
5415 if (trk->trackpoints == tpoints) {
5419 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5420 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5422 if (trk->trackpoints) {
5423 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5424 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5426 if (!p1->has_timestamp || !p2->has_timestamp) {
5427 //g_print("no timestamp\n");
5431 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5432 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5433 if (! (abs(t1 - p2->timestamp) < threshold ||
5435 abs(p1->timestamp - t2) < threshold)
5442 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5445 /* comparison function used to sort tracks; a and b are hash table keys */
5446 /* Not actively used - can be restored if needed
5447 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5449 GHashTable *tracks = user_data;
5452 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5453 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5455 if (t1 < t2) return -1;
5456 if (t1 > t2) return 1;
5461 /* comparison function used to sort trackpoints */
5462 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5464 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5466 if (t1 < t2) return -1;
5467 if (t1 > t2) return 1;
5472 * comparison function which can be used to sort tracks or waypoints by name
5474 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5476 const gchar* namea = (const gchar*) a;
5477 const gchar* nameb = (const gchar*) b;
5478 if ( namea == NULL || nameb == NULL)
5481 // Same sort method as used in the vik_treeview_*_alphabetize functions
5482 return strcmp ( namea, nameb );
5486 * Attempt to merge selected track with other tracks specified by the user
5487 * Tracks to merge with must be of the same 'type' as the selected track -
5488 * either all with timestamps, or all without timestamps
5490 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5492 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5493 GList *other_tracks = NULL;
5494 GHashTable *ght_tracks;
5495 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5496 ght_tracks = vtl->routes;
5498 ght_tracks = vtl->tracks;
5500 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5505 if ( !track->trackpoints )
5509 udata.result = &other_tracks;
5510 udata.exclude = track->trackpoints;
5511 // Allow merging with 'similar' time type time tracks
5512 // i.e. either those times, or those without
5513 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5515 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5516 other_tracks = g_list_reverse(other_tracks);
5518 if ( !other_tracks ) {
5519 if ( udata.with_timestamps )
5520 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5522 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5526 // Sort alphabetically for user presentation
5527 // Convert into list of names for usage with dialog function
5528 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5529 GList *other_tracks_names = NULL;
5530 GList *iter = g_list_first ( other_tracks );
5532 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5533 iter = g_list_next ( iter );
5536 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5538 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5542 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5543 g_list_free(other_tracks);
5544 g_list_free(other_tracks_names);
5549 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5550 VikTrack *merge_track;
5551 if ( track->is_route )
5552 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5554 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5557 vik_track_steal_and_append_trackpoints ( track, merge_track );
5558 if ( track->is_route )
5559 vik_trw_layer_delete_route (vtl, merge_track);
5561 vik_trw_layer_delete_track (vtl, merge_track);
5562 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5565 for (l = merge_list; l != NULL; l = g_list_next(l))
5567 g_list_free(merge_list);
5569 vik_layer_emit_update( VIK_LAYER(vtl) );
5573 // c.f. trw_layer_sorted_track_id_by_name_list
5574 // but don't add the specified track to the list (normally current track)
5575 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5577 twt_udata *user_data = udata;
5580 if (trk->trackpoints == user_data->exclude) {
5584 // Sort named list alphabetically
5585 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5589 * Join - this allows combining 'tracks' and 'track routes'
5590 * i.e. doesn't care about whether tracks have consistent timestamps
5591 * ATM can only append one track at a time to the currently selected track
5593 static void trw_layer_append_track ( gpointer pass_along[6] )
5596 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5598 GHashTable *ght_tracks;
5599 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5600 ght_tracks = vtl->routes;
5602 ght_tracks = vtl->tracks;
5604 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5609 GList *other_tracks_names = NULL;
5611 // Sort alphabetically for user presentation
5612 // Convert into list of names for usage with dialog function
5613 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5615 udata.result = &other_tracks_names;
5616 udata.exclude = trk->trackpoints;
5618 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5620 // Note the limit to selecting one track only
5621 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5622 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5623 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5626 trk->is_route ? _("Append Route"): _("Append Track"),
5627 trk->is_route ? _("Select the route to append after the current route") :
5628 _("Select the track to append after the current track") );
5630 g_list_free(other_tracks_names);
5632 // It's a list, but shouldn't contain more than one other track!
5633 if ( append_list ) {
5635 for (l = append_list; l != NULL; l = g_list_next(l)) {
5636 // TODO: at present this uses the first track found by name,
5637 // which with potential multiple same named tracks may not be the one selected...
5638 VikTrack *append_track;
5639 if ( trk->is_route )
5640 append_track = vik_trw_layer_get_route ( vtl, l->data );
5642 append_track = vik_trw_layer_get_track ( vtl, l->data );
5644 if ( append_track ) {
5645 vik_track_steal_and_append_trackpoints ( trk, append_track );
5646 if ( trk->is_route )
5647 vik_trw_layer_delete_route (vtl, append_track);
5649 vik_trw_layer_delete_track (vtl, append_track);
5652 for (l = append_list; l != NULL; l = g_list_next(l))
5654 g_list_free(append_list);
5656 vik_layer_emit_update( VIK_LAYER(vtl) );
5661 * Very similar to trw_layer_append_track for joining
5662 * but this allows selection from the 'other' list
5663 * If a track is selected, then is shows routes and joins the selected one
5664 * If a route is selected, then is shows tracks and joins the selected one
5666 static void trw_layer_append_other ( gpointer pass_along[6] )
5669 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5671 GHashTable *ght_mykind, *ght_others;
5672 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5673 ght_mykind = vtl->routes;
5674 ght_others = vtl->tracks;
5677 ght_mykind = vtl->tracks;
5678 ght_others = vtl->routes;
5681 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5686 GList *other_tracks_names = NULL;
5688 // Sort alphabetically for user presentation
5689 // Convert into list of names for usage with dialog function
5690 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5692 udata.result = &other_tracks_names;
5693 udata.exclude = trk->trackpoints;
5695 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5697 // Note the limit to selecting one track only
5698 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5699 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5700 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5703 trk->is_route ? _("Append Track"): _("Append Route"),
5704 trk->is_route ? _("Select the track to append after the current route") :
5705 _("Select the route to append after the current track") );
5707 g_list_free(other_tracks_names);
5709 // It's a list, but shouldn't contain more than one other track!
5710 if ( append_list ) {
5712 for (l = append_list; l != NULL; l = g_list_next(l)) {
5713 // TODO: at present this uses the first track found by name,
5714 // which with potential multiple same named tracks may not be the one selected...
5716 // Get FROM THE OTHER TYPE list
5717 VikTrack *append_track;
5718 if ( trk->is_route )
5719 append_track = vik_trw_layer_get_track ( vtl, l->data );
5721 append_track = vik_trw_layer_get_route ( vtl, l->data );
5723 if ( append_track ) {
5725 if ( !append_track->is_route &&
5726 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5727 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5729 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5730 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5731 vik_track_merge_segments ( append_track );
5732 vik_track_to_routepoints ( append_track );
5739 vik_track_steal_and_append_trackpoints ( trk, append_track );
5741 // Delete copied which is FROM THE OTHER TYPE list
5742 if ( trk->is_route )
5743 vik_trw_layer_delete_track (vtl, append_track);
5745 vik_trw_layer_delete_route (vtl, append_track);
5748 for (l = append_list; l != NULL; l = g_list_next(l))
5750 g_list_free(append_list);
5751 vik_layer_emit_update( VIK_LAYER(vtl) );
5755 /* merge by segments */
5756 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5758 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5759 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5760 guint segments = vik_track_merge_segments ( trk );
5761 // NB currently no need to redraw as segments not actually shown on the display
5762 // However inform the user of what happened:
5764 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5765 g_snprintf(str, 64, tmp_str, segments);
5766 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5769 /* merge by time routine */
5770 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5772 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5776 GList *tracks_with_timestamp = NULL;
5777 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5778 if (orig_trk->trackpoints &&
5779 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5780 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5785 udata.result = &tracks_with_timestamp;
5786 udata.exclude = orig_trk->trackpoints;
5787 udata.with_timestamps = TRUE;
5788 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5789 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5791 if (!tracks_with_timestamp) {
5792 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5795 g_list_free(tracks_with_timestamp);
5797 static guint threshold_in_minutes = 1;
5798 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5799 _("Merge Threshold..."),
5800 _("Merge when time between tracks less than:"),
5801 &threshold_in_minutes)) {
5805 // keep attempting to merge all tracks until no merges within the time specified is possible
5806 gboolean attempt_merge = TRUE;
5807 GList *nearby_tracks = NULL;
5809 static gpointer params[3];
5811 while ( attempt_merge ) {
5813 // Don't try again unless tracks have changed
5814 attempt_merge = FALSE;
5816 trps = orig_trk->trackpoints;
5820 if (nearby_tracks) {
5821 g_list_free(nearby_tracks);
5822 nearby_tracks = NULL;
5825 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5826 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5828 /* g_print("Original track times: %d and %d\n", t1, t2); */
5829 params[0] = &nearby_tracks;
5830 params[1] = (gpointer)trps;
5831 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5833 /* get a list of adjacent-in-time tracks */
5834 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5837 GList *l = nearby_tracks;
5840 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5841 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5843 t1 = get_first_trackpoint(l)->timestamp;
5844 t2 = get_last_trackpoint(l)->timestamp;
5845 #undef get_first_trackpoint
5846 #undef get_last_trackpoint
5847 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5850 /* remove trackpoints from merged track, delete track */
5851 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5852 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5854 // Tracks have changed, therefore retry again against all the remaining tracks
5855 attempt_merge = TRUE;
5860 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5863 g_list_free(nearby_tracks);
5865 vik_layer_emit_update( VIK_LAYER(vtl) );
5869 * Split a track at the currently selected trackpoint
5871 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5873 if ( !vtl->current_tpl )
5876 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5877 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5879 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5880 GList *newglist = g_list_alloc ();
5881 newglist->prev = NULL;
5882 newglist->next = vtl->current_tpl->next;
5883 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5884 tr->trackpoints = newglist;
5886 vtl->current_tpl->next->prev = newglist; /* end old track here */
5887 vtl->current_tpl->next = NULL;
5889 // Bounds of the selected track changed due to the split
5890 vik_track_calculate_bounds ( vtl->current_tp_track );
5892 vtl->current_tpl = newglist; /* change tp to first of new track. */
5893 vtl->current_tp_track = tr;
5896 vik_trw_layer_add_route ( vtl, name, tr );
5898 vik_trw_layer_add_track ( vtl, name, tr );
5900 // Bounds of the new track created by the split
5901 vik_track_calculate_bounds ( tr );
5907 // Also need id of newly created track
5910 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5912 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5914 if ( trkf && udata.uuid )
5915 vtl->current_tp_id = udata.uuid;
5917 vtl->current_tp_id = NULL;
5919 vik_layer_emit_update(VIK_LAYER(vtl));
5925 /* split by time routine */
5926 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5928 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5929 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5930 GList *trps = track->trackpoints;
5932 GList *newlists = NULL;
5933 GList *newtps = NULL;
5934 static guint thr = 1;
5941 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5942 _("Split Threshold..."),
5943 _("Split when time between trackpoints exceeds:"),
5948 /* iterate through trackpoints, and copy them into new lists without touching original list */
5949 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5953 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5955 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5958 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5959 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5960 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5962 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5967 if (ts - prev_ts > thr*60) {
5968 /* flush accumulated trackpoints into new list */
5969 newlists = g_list_append(newlists, g_list_reverse(newtps));
5973 /* accumulate trackpoint copies in newtps, in reverse order */
5974 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5976 iter = g_list_next(iter);
5979 newlists = g_list_append(newlists, g_list_reverse(newtps));
5982 /* put lists of trackpoints into tracks */
5984 // Only bother updating if the split results in new tracks
5985 if (g_list_length (newlists) > 1) {
5990 tr = vik_track_copy ( track, FALSE );
5991 tr->trackpoints = (GList *)(iter->data);
5993 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5994 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5995 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5996 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5997 g_free ( new_tr_name );
5998 vik_track_calculate_bounds ( tr );
5999 iter = g_list_next(iter);
6001 // Remove original track and then update the display
6002 vik_trw_layer_delete_track (vtl, track);
6003 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6005 g_list_free(newlists);
6009 * Split a track by the number of points as specified by the user
6011 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
6013 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6015 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6016 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6018 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6023 // Check valid track
6024 GList *trps = track->trackpoints;
6028 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6029 _("Split Every Nth Point"),
6030 _("Split on every Nth point:"),
6031 250, // Default value as per typical limited track capacity of various GPS devices
6035 // Was a valid number returned?
6041 GList *newlists = NULL;
6042 GList *newtps = NULL;
6047 /* accumulate trackpoint copies in newtps, in reverse order */
6048 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6050 if (count >= points) {
6051 /* flush accumulated trackpoints into new list */
6052 newlists = g_list_append(newlists, g_list_reverse(newtps));
6056 iter = g_list_next(iter);
6059 // If there is a remaining chunk put that into the new split list
6060 // This may well be the whole track if no split points were encountered
6062 newlists = g_list_append(newlists, g_list_reverse(newtps));
6065 /* put lists of trackpoints into tracks */
6067 // Only bother updating if the split results in new tracks
6068 if (g_list_length (newlists) > 1) {
6073 tr = vik_track_copy ( track, FALSE );
6074 tr->trackpoints = (GList *)(iter->data);
6076 if ( track->is_route ) {
6077 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6078 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6081 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6082 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6084 g_free ( new_tr_name );
6085 vik_track_calculate_bounds ( tr );
6087 iter = g_list_next(iter);
6089 // Remove original track and then update the display
6090 if ( track->is_route )
6091 vik_trw_layer_delete_route (vtl, track);
6093 vik_trw_layer_delete_track (vtl, track);
6094 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6096 g_list_free(newlists);
6100 * Split a track at the currently selected trackpoint
6102 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6104 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6105 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6106 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6110 * Split a track by its segments
6111 * Routes do not have segments so don't call this for routes
6113 static void trw_layer_split_segments ( gpointer pass_along[6] )
6115 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6116 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6123 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6126 for ( i = 0; i < ntracks; i++ ) {
6128 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6129 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6130 g_free ( new_tr_name );
6135 // Remove original track
6136 vik_trw_layer_delete_track ( vtl, trk );
6137 vik_layer_emit_update ( VIK_LAYER(vtl) );
6140 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6143 /* end of split/merge routines */
6145 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6149 // Find available adjacent trackpoint
6150 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6151 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6152 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6154 // Delete current trackpoint
6155 vik_trackpoint_free ( vtl->current_tpl->data );
6156 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6158 // Set to current to the available adjacent trackpoint
6159 vtl->current_tpl = new_tpl;
6161 if ( vtl->current_tp_track ) {
6162 vik_track_calculate_bounds ( vtl->current_tp_track );
6166 // Delete current trackpoint
6167 vik_trackpoint_free ( vtl->current_tpl->data );
6168 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6169 trw_layer_cancel_current_tp ( vtl, FALSE );
6174 * Delete the selected point
6176 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6178 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6180 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6181 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6183 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6188 if ( !vtl->current_tpl )
6191 trw_layer_trackpoint_selected_delete ( vtl, trk );
6193 // Track has been updated so update tps:
6194 trw_layer_cancel_tps_of_track ( vtl, trk );
6196 vik_layer_emit_update ( VIK_LAYER(vtl) );
6200 * Delete adjacent track points at the same position
6201 * AKA Delete Dulplicates on the Properties Window
6203 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6205 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6207 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6208 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6210 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6215 gulong removed = vik_track_remove_dup_points ( trk );
6217 // Track has been updated so update tps:
6218 trw_layer_cancel_tps_of_track ( vtl, trk );
6220 // Inform user how much was deleted as it's not obvious from the normal view
6222 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6223 g_snprintf(str, 64, tmp_str, removed);
6224 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6226 vik_layer_emit_update ( VIK_LAYER(vtl) );
6230 * Delete adjacent track points with the same timestamp
6231 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6233 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6235 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6237 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6238 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6240 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6245 gulong removed = vik_track_remove_same_time_points ( trk );
6247 // Track has been updated so update tps:
6248 trw_layer_cancel_tps_of_track ( vtl, trk );
6250 // Inform user how much was deleted as it's not obvious from the normal view
6252 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6253 g_snprintf(str, 64, tmp_str, removed);
6254 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6256 vik_layer_emit_update ( VIK_LAYER(vtl) );
6262 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6264 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6266 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6267 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6269 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6274 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6276 vik_layer_emit_update ( VIK_LAYER(vtl) );
6279 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6281 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6283 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6284 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6286 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6291 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6293 vik_layer_emit_update ( VIK_LAYER(vtl) );
6299 static void trw_layer_reverse ( gpointer pass_along[6] )
6301 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6303 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6304 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6306 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6311 vik_track_reverse ( track );
6313 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6317 * Similar to trw_layer_enum_item, but this uses a sorted method
6320 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6322 GList **list = (GList**)udata;
6323 // *list = g_list_prepend(*all, key); //unsorted method
6324 // Sort named list alphabetically
6325 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6330 * Now Waypoint specific sort
6332 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6334 GList **list = (GList**)udata;
6335 // Sort named list alphabetically
6336 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6340 * Track specific sort
6342 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6344 GList **list = (GList**)udata;
6345 // Sort named list alphabetically
6346 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6351 gboolean has_same_track_name;
6352 const gchar *same_track_name;
6353 } same_track_name_udata;
6355 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6357 const gchar* namea = (const gchar*) aa;
6358 const gchar* nameb = (const gchar*) bb;
6361 gint result = strcmp ( namea, nameb );
6363 if ( result == 0 ) {
6364 // Found two names the same
6365 same_track_name_udata *user_data = udata;
6366 user_data->has_same_track_name = TRUE;
6367 user_data->same_track_name = namea;
6370 // Leave ordering the same
6375 * Find out if any tracks have the same name in this hash table
6377 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6379 // Sort items by name, then compare if any next to each other are the same
6381 GList *track_names = NULL;
6382 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6385 if ( ! track_names )
6388 same_track_name_udata udata;
6389 udata.has_same_track_name = FALSE;
6391 // Use sort routine to traverse list comparing items
6392 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6393 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6394 // Still no tracks...
6398 return udata.has_same_track_name;
6402 * Force unqiue track names for the track table specified
6403 * Note the panel is a required parameter to enable the update of the names displayed
6404 * Specify if on tracks or else on routes
6406 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6408 // . Search list for an instance of repeated name
6409 // . get track of this name
6410 // . create new name
6411 // . rename track & update equiv. treeview iter
6412 // . repeat until all different
6414 same_track_name_udata udata;
6416 GList *track_names = NULL;
6417 udata.has_same_track_name = FALSE;
6418 udata.same_track_name = NULL;
6420 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6423 if ( ! track_names )
6426 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6428 // Still no tracks...
6429 if ( ! dummy_list1 )
6432 while ( udata.has_same_track_name ) {
6434 // Find a track with the same name
6437 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6439 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6443 g_critical("Houston, we've had a problem.");
6444 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6445 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6450 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6451 vik_track_set_name ( trk, newname );
6457 // Need want key of it for treeview update
6458 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6460 if ( trkf && udataU.uuid ) {
6464 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6466 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6469 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6471 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6473 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6477 // Start trying to find same names again...
6479 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6480 udata.has_same_track_name = FALSE;
6481 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6483 // No tracks any more - give up searching
6484 if ( ! dummy_list2 )
6485 udata.has_same_track_name = FALSE;
6489 vik_layers_panel_emit_update ( vlp );
6492 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6494 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6497 switch (GPOINTER_TO_INT (pass_along[2])) {
6498 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6499 iter = &(vtl->tracks_iter);
6500 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6502 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6503 iter = &(vtl->routes_iter);
6504 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6506 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6507 iter = &(vtl->waypoints_iter);
6508 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6512 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6515 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6517 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6520 switch (GPOINTER_TO_INT (pass_along[2])) {
6521 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6522 iter = &(vtl->tracks_iter);
6523 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6525 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6526 iter = &(vtl->routes_iter);
6527 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6529 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6530 iter = &(vtl->waypoints_iter);
6531 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6535 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6541 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6543 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6546 // Ensure list of track names offered is unique
6547 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6548 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6549 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6550 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6556 // Sort list alphabetically for better presentation
6557 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6560 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6564 // Get list of items to delete from the user
6565 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6568 _("Delete Selection"),
6569 _("Select tracks to delete"));
6572 // Delete requested tracks
6573 // since specificly requested, IMHO no need for extra confirmation
6574 if ( delete_list ) {
6576 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6577 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6578 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6580 g_list_free(delete_list);
6581 vik_layer_emit_update( VIK_LAYER(vtl) );
6588 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6590 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6593 // Ensure list of track names offered is unique
6594 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6595 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6596 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6597 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6603 // Sort list alphabetically for better presentation
6604 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6607 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6611 // Get list of items to delete from the user
6612 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6615 _("Delete Selection"),
6616 _("Select routes to delete") );
6619 // Delete requested routes
6620 // since specificly requested, IMHO no need for extra confirmation
6621 if ( delete_list ) {
6623 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6624 // This deletes first route it finds of that name (but uniqueness is enforced above)
6625 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6627 g_list_free(delete_list);
6628 vik_layer_emit_update( VIK_LAYER(vtl) );
6633 gboolean has_same_waypoint_name;
6634 const gchar *same_waypoint_name;
6635 } same_waypoint_name_udata;
6637 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6639 const gchar* namea = (const gchar*) aa;
6640 const gchar* nameb = (const gchar*) bb;
6643 gint result = strcmp ( namea, nameb );
6645 if ( result == 0 ) {
6646 // Found two names the same
6647 same_waypoint_name_udata *user_data = udata;
6648 user_data->has_same_waypoint_name = TRUE;
6649 user_data->same_waypoint_name = namea;
6652 // Leave ordering the same
6657 * Find out if any waypoints have the same name in this layer
6659 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6661 // Sort items by name, then compare if any next to each other are the same
6663 GList *waypoint_names = NULL;
6664 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6667 if ( ! waypoint_names )
6670 same_waypoint_name_udata udata;
6671 udata.has_same_waypoint_name = FALSE;
6673 // Use sort routine to traverse list comparing items
6674 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6675 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6676 // Still no waypoints...
6680 return udata.has_same_waypoint_name;
6684 * Force unqiue waypoint names for this layer
6685 * Note the panel is a required parameter to enable the update of the names displayed
6687 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6689 // . Search list for an instance of repeated name
6690 // . get waypoint of this name
6691 // . create new name
6692 // . rename waypoint & update equiv. treeview iter
6693 // . repeat until all different
6695 same_waypoint_name_udata udata;
6697 GList *waypoint_names = NULL;
6698 udata.has_same_waypoint_name = FALSE;
6699 udata.same_waypoint_name = NULL;
6701 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6704 if ( ! waypoint_names )
6707 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6709 // Still no waypoints...
6710 if ( ! dummy_list1 )
6713 while ( udata.has_same_waypoint_name ) {
6715 // Find a waypoint with the same name
6716 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6720 g_critical("Houston, we've had a problem.");
6721 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6722 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6727 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6729 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6731 // Start trying to find same names again...
6732 waypoint_names = NULL;
6733 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6734 udata.has_same_waypoint_name = FALSE;
6735 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6737 // No waypoints any more - give up searching
6738 if ( ! dummy_list2 )
6739 udata.has_same_waypoint_name = FALSE;
6743 vik_layers_panel_emit_update ( vlp );
6749 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6751 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6754 // Ensure list of waypoint names offered is unique
6755 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6756 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6757 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6758 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6764 // Sort list alphabetically for better presentation
6765 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6767 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6771 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6773 // Get list of items to delete from the user
6774 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6777 _("Delete Selection"),
6778 _("Select waypoints to delete"));
6781 // Delete requested waypoints
6782 // since specificly requested, IMHO no need for extra confirmation
6783 if ( delete_list ) {
6785 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6786 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6787 trw_layer_delete_waypoint_by_name (vtl, l->data);
6789 g_list_free(delete_list);
6791 trw_layer_calculate_bounds_waypoints ( vtl );
6792 vik_layer_emit_update( VIK_LAYER(vtl) );
6800 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6802 vik_treeview_item_toggle_visible ( vt, it );
6808 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6810 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6816 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6818 wp->visible = GPOINTER_TO_INT (on_off);
6824 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6826 wp->visible = !wp->visible;
6832 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6834 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6835 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6836 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6837 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6839 vik_layer_emit_update ( VIK_LAYER(vtl) );
6845 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6847 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6848 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6849 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6850 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6852 vik_layer_emit_update ( VIK_LAYER(vtl) );
6858 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6860 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6861 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6862 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6864 vik_layer_emit_update ( VIK_LAYER(vtl) );
6870 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6872 trk->visible = GPOINTER_TO_INT (on_off);
6878 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6880 trk->visible = !trk->visible;
6886 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6888 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6889 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6890 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6891 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6893 vik_layer_emit_update ( VIK_LAYER(vtl) );
6899 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6901 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6902 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6903 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6904 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6906 vik_layer_emit_update ( VIK_LAYER(vtl) );
6912 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6914 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6915 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6916 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6918 vik_layer_emit_update ( VIK_LAYER(vtl) );
6924 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6926 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6927 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6928 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6929 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6931 vik_layer_emit_update ( VIK_LAYER(vtl) );
6937 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6939 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6940 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6941 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6942 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6944 vik_layer_emit_update ( VIK_LAYER(vtl) );
6950 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6952 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6953 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6954 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6956 vik_layer_emit_update ( VIK_LAYER(vtl) );
6960 * vik_trw_layer_build_waypoint_list_t:
6962 * Helper function to construct a list of #vik_trw_waypoint_list_t
6964 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
6966 GList *waypoints_and_layers = NULL;
6967 // build waypoints_and_layers list
6968 while ( waypoints ) {
6969 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
6970 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
6972 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
6973 waypoints = g_list_next ( waypoints );
6975 return waypoints_and_layers;
6979 * trw_layer_create_waypoint_list:
6981 * Create the latest list of waypoints with the associated layer(s)
6982 * Although this will always be from a single layer here
6984 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
6986 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6987 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
6989 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
6993 * trw_layer_analyse_close:
6995 * Stuff to do on dialog closure
6997 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6999 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7000 gtk_widget_destroy ( dialog );
7001 vtl->tracks_analysis_dialog = NULL;
7005 * vik_trw_layer_build_track_list_t:
7007 * Helper function to construct a list of #vik_trw_track_list_t
7009 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7011 GList *tracks_and_layers = NULL;
7012 // build tracks_and_layers list
7014 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7015 vtdl->trk = VIK_TRACK(tracks->data);
7017 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7018 tracks = g_list_next ( tracks );
7020 return tracks_and_layers;
7024 * trw_layer_create_track_list:
7026 * Create the latest list of tracks with the associated layer(s)
7027 * Although this will always be from a single layer here
7029 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7031 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7032 GList *tracks = NULL;
7033 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7034 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7036 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7038 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7041 static void trw_layer_tracks_stats ( gpointer lav[2] )
7043 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7044 // There can only be one!
7045 if ( vtl->tracks_analysis_dialog )
7048 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7049 VIK_LAYER(vtl)->name,
7051 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7052 trw_layer_create_track_list,
7053 trw_layer_analyse_close );
7059 static void trw_layer_routes_stats ( gpointer lav[2] )
7061 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7062 // There can only be one!
7063 if ( vtl->tracks_analysis_dialog )
7066 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7067 VIK_LAYER(vtl)->name,
7069 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7070 trw_layer_create_track_list,
7071 trw_layer_analyse_close );
7074 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
7076 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7078 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
7081 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
7083 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7086 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7087 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7091 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
7093 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7096 if ( !strncmp(wp->comment, "http", 4) ) {
7097 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7098 } else if ( !strncmp(wp->description, "http", 4) ) {
7099 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7103 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7105 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7107 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7109 // No actual change to the name supplied
7111 if (strcmp(newname, wp->name) == 0 )
7114 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7117 // An existing waypoint has been found with the requested name
7118 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7119 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7124 // Update WP name and refresh the treeview
7125 vik_waypoint_set_name (wp, newname);
7127 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7128 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7130 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7135 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7137 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7139 // No actual change to the name supplied
7141 if (strcmp(newname, trk->name) == 0)
7144 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7147 // An existing track has been found with the requested name
7148 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7149 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7153 // Update track name and refresh GUI parts
7154 vik_track_set_name (trk, newname);
7156 // Update any subwindows that could be displaying this track which has changed name
7157 // Only one Track Edit Window
7158 if ( l->current_tp_track == trk && l->tpwin ) {
7159 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7161 // Property Dialog of the track
7162 vik_trw_layer_propwin_update ( trk );
7164 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7165 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7167 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7172 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7174 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7176 // No actual change to the name supplied
7178 if (strcmp(newname, trk->name) == 0)
7181 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7184 // An existing track has been found with the requested name
7185 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7186 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7190 // Update track name and refresh GUI parts
7191 vik_track_set_name (trk, newname);
7193 // Update any subwindows that could be displaying this track which has changed name
7194 // Only one Track Edit Window
7195 if ( l->current_tp_track == trk && l->tpwin ) {
7196 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7198 // Property Dialog of the track
7199 vik_trw_layer_propwin_update ( trk );
7201 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7202 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7204 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7211 static gboolean is_valid_geocache_name ( gchar *str )
7213 gint len = strlen ( str );
7214 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]));
7217 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7219 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7220 a_acquire_set_filter_track ( trk );
7223 #ifdef VIK_CONFIG_GOOGLE
7224 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7226 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7227 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7230 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7232 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7234 gchar *escaped = uri_escape ( tr->comment );
7235 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7236 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7243 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7244 /* viewpoint is now available instead */
7245 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7247 static gpointer pass_along[8];
7249 gboolean rv = FALSE;
7252 pass_along[1] = vlp;
7253 pass_along[2] = GINT_TO_POINTER (subtype);
7254 pass_along[3] = sublayer;
7255 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7256 pass_along[5] = vvp;
7257 pass_along[6] = iter;
7258 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7260 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7264 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7265 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7266 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7267 gtk_widget_show ( item );
7269 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7270 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7271 if (tr && tr->property_dialog)
7272 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7274 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7275 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7276 if (tr && tr->property_dialog)
7277 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7280 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7281 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7282 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7283 gtk_widget_show ( item );
7285 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7287 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7288 gtk_widget_show ( item );
7290 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7292 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7293 gtk_widget_show ( item );
7295 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7297 // Always create separator as now there is always at least the transform menu option
7298 item = gtk_menu_item_new ();
7299 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7300 gtk_widget_show ( item );
7302 /* could be a right-click using the tool */
7303 if ( vlp != NULL ) {
7304 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7307 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7308 gtk_widget_show ( item );
7311 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7313 if ( wp && wp->name ) {
7314 if ( is_valid_geocache_name ( wp->name ) ) {
7315 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7317 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7318 gtk_widget_show ( item );
7320 #ifdef VIK_CONFIG_GEOTAG
7321 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7323 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7324 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7325 gtk_widget_show ( item );
7329 if ( wp && wp->image )
7331 // Set up image paramater
7332 pass_along[5] = wp->image;
7334 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7335 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
7336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7337 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7338 gtk_widget_show ( item );
7340 #ifdef VIK_CONFIG_GEOTAG
7341 GtkWidget *geotag_submenu = gtk_menu_new ();
7342 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7343 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7344 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7345 gtk_widget_show ( item );
7346 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7348 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7350 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7351 gtk_widget_show ( item );
7353 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7355 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7356 gtk_widget_show ( item );
7362 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7363 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7364 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7365 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7367 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7368 gtk_widget_show ( item );
7374 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7375 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7376 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7377 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7378 gtk_widget_show ( item );
7379 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7380 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7381 gtk_widget_set_sensitive ( item, TRUE );
7383 gtk_widget_set_sensitive ( item, FALSE );
7386 item = gtk_menu_item_new ();
7387 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7388 gtk_widget_show ( item );
7391 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7394 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7395 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7397 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7398 gtk_widget_show ( item );
7401 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7403 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7404 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7406 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7407 gtk_widget_show ( item );
7409 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7410 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7412 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7413 gtk_widget_show ( item );
7415 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7416 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7417 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7418 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7419 gtk_widget_show ( item );
7421 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7422 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7423 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7424 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7425 gtk_widget_show ( item );
7427 GtkWidget *vis_submenu = gtk_menu_new ();
7428 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7429 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7430 gtk_widget_show ( item );
7431 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7433 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7434 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7436 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7437 gtk_widget_show ( item );
7439 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7440 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7442 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7443 gtk_widget_show ( item );
7445 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7446 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7447 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7448 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7449 gtk_widget_show ( item );
7451 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7452 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7453 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7454 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7457 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7461 if ( l->current_track && !l->current_track->is_route ) {
7462 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7463 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7464 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7465 gtk_widget_show ( item );
7467 item = gtk_menu_item_new ();
7468 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7469 gtk_widget_show ( item );
7472 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7473 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7475 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7476 gtk_widget_show ( item );
7478 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7479 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7481 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7482 gtk_widget_show ( item );
7483 // Make it available only when a new track *not* already in progress
7484 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7486 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7487 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7490 gtk_widget_show ( item );
7492 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7493 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7494 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7495 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7496 gtk_widget_show ( item );
7498 GtkWidget *vis_submenu = gtk_menu_new ();
7499 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7500 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7501 gtk_widget_show ( item );
7502 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7505 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7507 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7508 gtk_widget_show ( item );
7510 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7511 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7512 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7513 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7514 gtk_widget_show ( item );
7516 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7517 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7518 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7519 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7521 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7522 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7523 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7524 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7525 gtk_widget_show ( item );
7527 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7529 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7530 gtk_widget_show ( item );
7533 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7537 if ( l->current_track && l->current_track->is_route ) {
7538 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7539 // Reuse finish track method
7540 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7541 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7542 gtk_widget_show ( item );
7544 item = gtk_menu_item_new ();
7545 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7546 gtk_widget_show ( item );
7549 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7550 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7551 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7552 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7553 gtk_widget_show ( item );
7555 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7556 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7557 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7558 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7559 gtk_widget_show ( item );
7560 // Make it available only when a new track *not* already in progress
7561 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7563 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7564 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7565 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7566 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7567 gtk_widget_show ( item );
7569 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7570 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7573 gtk_widget_show ( item );
7575 GtkWidget *vis_submenu = gtk_menu_new ();
7576 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7577 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7578 gtk_widget_show ( item );
7579 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7581 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7582 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7583 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7584 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7585 gtk_widget_show ( item );
7587 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7588 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7591 gtk_widget_show ( item );
7593 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7594 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7595 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7596 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7598 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7599 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7600 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7601 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7603 gtk_widget_show ( item );
7605 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7606 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7607 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7608 gtk_widget_show ( item );
7612 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7613 GtkWidget *submenu_sort = gtk_menu_new ();
7614 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7615 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7616 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7617 gtk_widget_show ( item );
7618 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7620 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7621 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7623 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7624 gtk_widget_show ( item );
7626 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7627 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7628 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7629 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7630 gtk_widget_show ( item );
7633 GtkWidget *upload_submenu = gtk_menu_new ();
7635 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7637 item = gtk_menu_item_new ();
7638 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7639 gtk_widget_show ( item );
7641 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7642 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7643 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7644 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7645 if ( l->current_track ) {
7646 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7647 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7648 gtk_widget_show ( item );
7651 item = gtk_menu_item_new ();
7652 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7653 gtk_widget_show ( item );
7656 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7657 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7659 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7660 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7662 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7663 gtk_widget_show ( item );
7665 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7667 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7668 gtk_widget_show ( item );
7670 GtkWidget *goto_submenu;
7671 goto_submenu = gtk_menu_new ();
7672 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7673 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7674 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7675 gtk_widget_show ( item );
7676 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7678 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7679 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7680 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7681 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7682 gtk_widget_show ( item );
7684 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7685 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7686 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7687 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7688 gtk_widget_show ( item );
7690 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7691 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7692 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7693 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7694 gtk_widget_show ( item );
7696 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7697 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7698 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7699 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7700 gtk_widget_show ( item );
7702 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7705 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7706 gtk_widget_show ( item );
7708 // Routes don't have speeds
7709 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7710 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7711 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7713 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7714 gtk_widget_show ( item );
7717 GtkWidget *combine_submenu;
7718 combine_submenu = gtk_menu_new ();
7719 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7721 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7722 gtk_widget_show ( item );
7723 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7725 // Routes don't have times or segments...
7726 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7727 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7729 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7730 gtk_widget_show ( item );
7732 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7734 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7735 gtk_widget_show ( item );
7738 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7739 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7740 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7741 gtk_widget_show ( item );
7743 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7744 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7746 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7747 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7748 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7749 gtk_widget_show ( item );
7751 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7752 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7754 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7755 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7756 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7757 gtk_widget_show ( item );
7759 GtkWidget *split_submenu;
7760 split_submenu = gtk_menu_new ();
7761 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7762 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7763 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7764 gtk_widget_show ( item );
7765 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7767 // Routes don't have times or segments...
7768 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7769 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7770 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7771 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7772 gtk_widget_show ( item );
7774 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7775 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7777 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7778 gtk_widget_show ( item );
7781 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7782 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7783 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7784 gtk_widget_show ( item );
7786 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7788 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7789 gtk_widget_show ( item );
7790 // Make it available only when a trackpoint is selected.
7791 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7793 GtkWidget *insert_submenu = gtk_menu_new ();
7794 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7795 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7796 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7797 gtk_widget_show ( item );
7798 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7800 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7801 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7802 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7803 gtk_widget_show ( item );
7804 // Make it available only when a point is selected
7805 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7807 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7808 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7809 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7810 gtk_widget_show ( item );
7811 // Make it available only when a point is selected
7812 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7814 GtkWidget *delete_submenu;
7815 delete_submenu = gtk_menu_new ();
7816 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7818 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7819 gtk_widget_show ( item );
7820 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7825 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7826 gtk_widget_show ( item );
7827 // Make it available only when a point is selected
7828 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7830 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7832 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7833 gtk_widget_show ( item );
7835 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7837 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7838 gtk_widget_show ( item );
7840 GtkWidget *transform_submenu;
7841 transform_submenu = gtk_menu_new ();
7842 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7843 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7844 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7845 gtk_widget_show ( item );
7846 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7848 GtkWidget *dem_submenu;
7849 dem_submenu = gtk_menu_new ();
7850 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7851 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
7852 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7853 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7855 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7856 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7857 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7858 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7859 gtk_widget_show ( item );
7861 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7862 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7863 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7864 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7865 gtk_widget_show ( item );
7867 GtkWidget *smooth_submenu;
7868 smooth_submenu = gtk_menu_new ();
7869 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7870 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7871 gtk_widget_show ( item );
7872 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7874 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7875 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7876 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7877 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7878 gtk_widget_show ( item );
7880 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7881 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7882 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7883 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7884 gtk_widget_show ( item );
7886 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7887 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7889 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7890 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7891 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7892 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7893 gtk_widget_show ( item );
7895 // Routes don't have timestamps - so this is only available for tracks
7896 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7897 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7899 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7900 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7901 gtk_widget_show ( item );
7904 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7905 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7907 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7908 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7909 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7910 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7911 gtk_widget_show ( item );
7913 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7914 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7915 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7916 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7917 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7918 gtk_widget_show ( item );
7921 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7923 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7924 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7926 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7927 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
7928 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7929 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7930 gtk_widget_show ( item );
7933 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7934 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7936 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7937 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7938 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7939 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7940 gtk_widget_show ( item );
7942 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7943 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7945 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7946 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7947 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7948 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7949 gtk_widget_show ( item );
7951 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7952 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7953 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
7954 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7955 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7956 gtk_widget_show ( item );
7959 // ATM can't upload a single waypoint but can do waypoints to a GPS
7960 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7961 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7962 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7963 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7964 gtk_widget_show ( item );
7965 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7967 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7968 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7970 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7971 gtk_widget_show ( item );
7975 #ifdef VIK_CONFIG_GOOGLE
7976 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7978 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7979 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7980 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7981 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7982 gtk_widget_show ( item );
7986 // Some things aren't usable with routes
7987 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7988 #ifdef VIK_CONFIG_OPENSTREETMAP
7989 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7990 // Convert internal pointer into actual track for usage outside this file
7991 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7992 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7994 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7995 gtk_widget_show ( item );
7998 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7999 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8001 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8002 gtk_widget_show ( item );
8004 /* ATM This function is only available via the layers panel, due to needing a vlp */
8006 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8007 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8008 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8010 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8011 gtk_widget_show ( item );
8015 #ifdef VIK_CONFIG_GEOTAG
8016 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8018 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8019 gtk_widget_show ( item );
8023 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8024 // Only show on viewport popmenu when a trackpoint is selected
8025 if ( ! vlp && l->current_tpl ) {
8027 item = gtk_menu_item_new ();
8028 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8029 gtk_widget_show ( item );
8031 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8032 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8034 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8035 gtk_widget_show ( item );
8039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8040 GtkWidget *transform_submenu;
8041 transform_submenu = gtk_menu_new ();
8042 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8043 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8044 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8045 gtk_widget_show ( item );
8046 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8048 GtkWidget *dem_submenu;
8049 dem_submenu = gtk_menu_new ();
8050 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8051 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
8052 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8053 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8055 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8056 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8057 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8058 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8059 gtk_widget_show ( item );
8061 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8063 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8064 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8065 gtk_widget_show ( item );
8068 gtk_widget_show_all ( GTK_WIDGET(menu) );
8073 // TODO: Probably better to rework this track manipulation in viktrack.c
8074 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8077 if (!vtl->current_tpl)
8080 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8081 VikTrackpoint *tp_other = NULL;
8084 if (!vtl->current_tpl->prev)
8086 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8088 if (!vtl->current_tpl->next)
8090 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8093 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8096 VikTrackpoint *tp_new = vik_trackpoint_new();
8097 struct LatLon ll_current, ll_other;
8098 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8099 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8101 /* main positional interpolation */
8102 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8103 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8105 /* Now other properties that can be interpolated */
8106 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8108 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8109 /* Note here the division is applied to each part, then added
8110 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8111 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8112 tp_new->has_timestamp = TRUE;
8115 if (tp_current->speed != NAN && tp_other->speed != NAN)
8116 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8118 /* TODO - improve interpolation of course, as it may not be correct.
8119 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8120 [similar applies if value is in radians] */
8121 if (tp_current->course != NAN && tp_other->course != NAN)
8122 tp_new->course = (tp_current->course + tp_other->course)/2;
8124 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8126 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8127 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8129 // Otherwise try routes
8130 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8134 gint index = g_list_index ( trk->trackpoints, tp_current );
8138 // NB no recalculation of bounds since it is inserted between points
8139 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8144 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8150 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8154 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8156 if ( vtl->current_tpl )
8158 vtl->current_tpl = NULL;
8159 vtl->current_tp_track = NULL;
8160 vtl->current_tp_id = NULL;
8161 vik_layer_emit_update(VIK_LAYER(vtl));
8165 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8167 g_assert ( vtl->tpwin != NULL );
8168 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8169 trw_layer_cancel_current_tp ( vtl, TRUE );
8171 if ( vtl->current_tpl == NULL )
8174 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8176 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8177 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8179 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8181 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8183 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8187 trw_layer_trackpoint_selected_delete ( vtl, tr );
8189 if ( vtl->current_tpl )
8190 // Reset dialog with the available adjacent trackpoint
8191 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8193 vik_layer_emit_update(VIK_LAYER(vtl));
8195 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8197 if ( vtl->current_tp_track )
8198 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8199 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8201 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8203 if ( vtl->current_tp_track )
8204 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8205 vik_layer_emit_update(VIK_LAYER(vtl));
8207 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8209 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8210 vik_layer_emit_update(VIK_LAYER(vtl));
8212 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8213 vik_layer_emit_update(VIK_LAYER(vtl));
8217 * trw_layer_dialog_shift:
8218 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8220 * Try to reposition a dialog if it's over the specified coord
8221 * so to not obscure the item of interest
8223 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8225 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8227 // Attempt force dialog to be shown so we can find out where it is more reliably...
8228 while ( gtk_events_pending() )
8229 gtk_main_iteration ();
8231 // get parent window position & size
8232 gint win_pos_x, win_pos_y;
8233 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8235 gint win_size_x, win_size_y;
8236 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8238 // get own dialog size
8239 gint dia_size_x, dia_size_y;
8240 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8242 // get own dialog position
8243 gint dia_pos_x, dia_pos_y;
8244 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8246 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8247 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8249 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8251 gint vp_xx, vp_yy; // In viewport pixels
8252 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8254 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8258 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8260 // Transform Viewport pixels into absolute pixels
8261 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8262 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8264 // Is dialog over the point (to within an ^^ edge value)
8265 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8266 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8270 gint hh = vik_viewport_get_height ( vvp );
8272 // Consider the difference in viewport to the full window
8273 gint offset_y = dest_y;
8274 // Add difference between dialog and window sizes
8275 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8277 if ( vp_yy > hh/2 ) {
8278 // Point in bottom half, move window to top half
8279 gtk_window_move ( dialog, dia_pos_x, offset_y );
8282 // Point in top half, move dialog down
8283 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8287 // Shift left<->right
8288 gint ww = vik_viewport_get_width ( vvp );
8290 // Consider the difference in viewport to the full window
8291 gint offset_x = dest_x;
8292 // Add difference between dialog and window sizes
8293 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8295 if ( vp_xx > ww/2 ) {
8296 // Point on right, move window to left
8297 gtk_window_move ( dialog, offset_x, dia_pos_y );
8300 // Point on left, move right
8301 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8309 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8313 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8314 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8315 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8316 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8318 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8320 if ( vtl->current_tpl ) {
8321 // get tp pixel position
8322 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8324 // Shift up<->down to try not to obscure the trackpoint.
8325 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8329 if ( vtl->current_tpl )
8330 if ( vtl->current_tp_track )
8331 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8332 /* set layer name and TP data */
8335 /***************************************************************************
8337 ***************************************************************************/
8339 /*** Utility data structures and functions ****/
8343 gint closest_x, closest_y;
8344 gboolean draw_images;
8345 gpointer *closest_wp_id;
8346 VikWaypoint *closest_wp;
8352 gint closest_x, closest_y;
8353 gpointer closest_track_id;
8354 VikTrackpoint *closest_tp;
8360 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8366 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8368 // If waypoint has an image then use the image size to select
8369 if ( params->draw_images && wp->image ) {
8370 gint slackx, slacky;
8371 slackx = wp->image_width / 2;
8372 slacky = wp->image_height / 2;
8374 if ( x <= params->x + slackx && x >= params->x - slackx
8375 && y <= params->y + slacky && y >= params->y - slacky ) {
8376 params->closest_wp_id = id;
8377 params->closest_wp = wp;
8378 params->closest_x = x;
8379 params->closest_y = y;
8382 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8383 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8384 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8386 params->closest_wp_id = id;
8387 params->closest_wp = wp;
8388 params->closest_x = x;
8389 params->closest_y = y;
8393 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8395 GList *tpl = t->trackpoints;
8401 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8407 tp = VIK_TRACKPOINT(tpl->data);
8409 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8411 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8412 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8413 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8415 params->closest_track_id = id;
8416 params->closest_tp = tp;
8417 params->closest_tpl = tpl;
8418 params->closest_x = x;
8419 params->closest_y = y;
8425 // ATM: Leave this as 'Track' only.
8426 // Not overly bothered about having a snap to route trackpoint capability
8427 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8429 TPSearchParams params;
8433 params.closest_track_id = NULL;
8434 params.closest_tp = NULL;
8435 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8436 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8437 return params.closest_tp;
8440 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8442 WPSearchParams params;
8446 params.draw_images = vtl->drawimages;
8447 params.closest_wp = NULL;
8448 params.closest_wp_id = NULL;
8449 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8450 return params.closest_wp;
8454 // Some forward declarations
8455 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8456 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8457 static void marker_end_move ( tool_ed_t *t );
8460 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8464 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8466 // Here always allow snapping back to the original location
8467 // this is useful when one decides not to move the thing afterall
8468 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8471 if ( event->state & GDK_CONTROL_MASK )
8473 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8475 new_coord = tp->coord;
8479 if ( event->state & GDK_SHIFT_MASK )
8481 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8483 new_coord = wp->coord;
8487 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8489 marker_moveto ( t, x, y );
8496 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8498 if ( t->holding && event->button == 1 )
8501 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8504 if ( event->state & GDK_CONTROL_MASK )
8506 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8508 new_coord = tp->coord;
8512 if ( event->state & GDK_SHIFT_MASK )
8514 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8516 new_coord = wp->coord;
8519 marker_end_move ( t );
8521 // Determine if working on a waypoint or a trackpoint
8522 if ( t->is_waypoint ) {
8523 // Update waypoint position
8524 vtl->current_wp->coord = new_coord;
8525 trw_layer_calculate_bounds_waypoints ( vtl );
8526 // Reset waypoint pointer
8527 vtl->current_wp = NULL;
8528 vtl->current_wp_id = NULL;
8531 if ( vtl->current_tpl ) {
8532 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8534 if ( vtl->current_tp_track )
8535 vik_track_calculate_bounds ( vtl->current_tp_track );
8538 if ( vtl->current_tp_track )
8539 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8540 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8544 vik_layer_emit_update ( VIK_LAYER(vtl) );
8551 Returns true if a waypoint or track is found near the requested event position for this particular layer
8552 The item found is automatically selected
8553 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8555 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8557 if ( event->button != 1 )
8560 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8563 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8567 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8569 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8571 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8572 WPSearchParams wp_params;
8573 wp_params.vvp = vvp;
8574 wp_params.x = event->x;
8575 wp_params.y = event->y;
8576 wp_params.draw_images = vtl->drawimages;
8577 wp_params.closest_wp_id = NULL;
8578 wp_params.closest_wp = NULL;
8580 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8582 if ( wp_params.closest_wp ) {
8585 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8587 // Too easy to move it so must be holding shift to start immediately moving it
8588 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8589 if ( event->state & GDK_SHIFT_MASK ||
8590 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8591 // Put into 'move buffer'
8592 // NB vvp & vw already set in tet
8593 tet->vtl = (gpointer)vtl;
8594 tet->is_waypoint = TRUE;
8596 marker_begin_move (tet, event->x, event->y);
8599 vtl->current_wp = wp_params.closest_wp;
8600 vtl->current_wp_id = wp_params.closest_wp_id;
8602 vik_layer_emit_update ( VIK_LAYER(vtl) );
8608 // Used for both track and route lists
8609 TPSearchParams tp_params;
8610 tp_params.vvp = vvp;
8611 tp_params.x = event->x;
8612 tp_params.y = event->y;
8613 tp_params.closest_track_id = NULL;
8614 tp_params.closest_tp = NULL;
8615 tp_params.closest_tpl = NULL;
8616 tp_params.bbox = bbox;
8618 if (vtl->tracks_visible) {
8619 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8621 if ( tp_params.closest_tp ) {
8623 // Always select + highlight the track
8624 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8626 tet->is_waypoint = FALSE;
8628 // Select the Trackpoint
8629 // Can move it immediately when control held or it's the previously selected tp
8630 if ( event->state & GDK_CONTROL_MASK ||
8631 vtl->current_tpl == tp_params.closest_tpl ) {
8632 // Put into 'move buffer'
8633 // NB vvp & vw already set in tet
8634 tet->vtl = (gpointer)vtl;
8635 marker_begin_move (tet, event->x, event->y);
8638 vtl->current_tpl = tp_params.closest_tpl;
8639 vtl->current_tp_id = tp_params.closest_track_id;
8640 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8642 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8645 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8647 vik_layer_emit_update ( VIK_LAYER(vtl) );
8652 // Try again for routes
8653 if (vtl->routes_visible) {
8654 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8656 if ( tp_params.closest_tp ) {
8658 // Always select + highlight the track
8659 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8661 tet->is_waypoint = FALSE;
8663 // Select the Trackpoint
8664 // Can move it immediately when control held or it's the previously selected tp
8665 if ( event->state & GDK_CONTROL_MASK ||
8666 vtl->current_tpl == tp_params.closest_tpl ) {
8667 // Put into 'move buffer'
8668 // NB vvp & vw already set in tet
8669 tet->vtl = (gpointer)vtl;
8670 marker_begin_move (tet, event->x, event->y);
8673 vtl->current_tpl = tp_params.closest_tpl;
8674 vtl->current_tp_id = tp_params.closest_track_id;
8675 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8677 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8680 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8682 vik_layer_emit_update ( VIK_LAYER(vtl) );
8687 /* these aren't the droids you're looking for */
8688 vtl->current_wp = NULL;
8689 vtl->current_wp_id = NULL;
8690 trw_layer_cancel_current_tp ( vtl, FALSE );
8693 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8698 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8700 if ( event->button != 3 )
8703 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8706 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8709 /* Post menu for the currently selected item */
8711 /* See if a track is selected */
8712 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8713 if ( track && track->visible ) {
8715 if ( track->name ) {
8717 if ( vtl->track_right_click_menu )
8718 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8720 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8727 if ( track->is_route )
8728 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8730 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8732 if ( trkf && udataU.uuid ) {
8735 if ( track->is_route )
8736 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8738 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8740 trw_layer_sublayer_add_menu_items ( vtl,
8741 vtl->track_right_click_menu,
8743 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8749 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8755 /* See if a waypoint is selected */
8756 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8757 if ( waypoint && waypoint->visible ) {
8758 if ( waypoint->name ) {
8760 if ( vtl->wp_right_click_menu )
8761 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8763 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8766 udata.wp = waypoint;
8769 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8771 if ( wpf && udata.uuid ) {
8772 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8774 trw_layer_sublayer_add_menu_items ( vtl,
8775 vtl->wp_right_click_menu,
8777 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8782 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8791 /* background drawing hook, to be passed the viewport */
8792 static gboolean tool_sync_done = TRUE;
8794 static gboolean tool_sync(gpointer data)
8796 VikViewport *vvp = data;
8797 gdk_threads_enter();
8798 vik_viewport_sync(vvp);
8799 tool_sync_done = TRUE;
8800 gdk_threads_leave();
8804 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8807 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8808 gdk_gc_set_function ( t->gc, GDK_INVERT );
8809 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8810 vik_viewport_sync(t->vvp);
8815 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8817 VikViewport *vvp = t->vvp;
8818 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8819 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8823 if (tool_sync_done) {
8824 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8825 tool_sync_done = FALSE;
8829 static void marker_end_move ( tool_ed_t *t )
8831 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8832 g_object_unref ( t->gc );
8836 /*** Edit waypoint ****/
8838 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8840 tool_ed_t *t = g_new(tool_ed_t, 1);
8846 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8851 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8853 WPSearchParams params;
8854 tool_ed_t *t = data;
8855 VikViewport *vvp = t->vvp;
8857 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8864 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8867 if ( vtl->current_wp && vtl->current_wp->visible )
8869 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8871 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8873 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8874 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8876 if ( event->button == 3 )
8877 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8879 marker_begin_move(t, event->x, event->y);
8886 params.x = event->x;
8887 params.y = event->y;
8888 params.draw_images = vtl->drawimages;
8889 params.closest_wp_id = NULL;
8890 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8891 params.closest_wp = NULL;
8892 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8893 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8895 // how do we get here?
8896 marker_begin_move(t, event->x, event->y);
8897 g_critical("shouldn't be here");
8900 else if ( params.closest_wp )
8902 if ( event->button == 3 )
8903 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8905 vtl->waypoint_rightclick = FALSE;
8907 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8909 vtl->current_wp = params.closest_wp;
8910 vtl->current_wp_id = params.closest_wp_id;
8912 /* could make it so don't update if old WP is off screen and new is null but oh well */
8913 vik_layer_emit_update ( VIK_LAYER(vtl) );
8917 vtl->current_wp = NULL;
8918 vtl->current_wp_id = NULL;
8919 vtl->waypoint_rightclick = FALSE;
8920 vik_layer_emit_update ( VIK_LAYER(vtl) );
8924 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8926 tool_ed_t *t = data;
8927 VikViewport *vvp = t->vvp;
8929 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8934 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8937 if ( event->state & GDK_CONTROL_MASK )
8939 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8941 new_coord = tp->coord;
8945 if ( event->state & GDK_SHIFT_MASK )
8947 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8948 if ( wp && wp != vtl->current_wp )
8949 new_coord = wp->coord;
8954 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8956 marker_moveto ( t, x, y );
8963 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8965 tool_ed_t *t = data;
8966 VikViewport *vvp = t->vvp;
8968 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8971 if ( t->holding && event->button == 1 )
8974 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8977 if ( event->state & GDK_CONTROL_MASK )
8979 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8981 new_coord = tp->coord;
8985 if ( event->state & GDK_SHIFT_MASK )
8987 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8988 if ( wp && wp != vtl->current_wp )
8989 new_coord = wp->coord;
8992 marker_end_move ( t );
8994 vtl->current_wp->coord = new_coord;
8996 trw_layer_calculate_bounds_waypoints ( vtl );
8997 vik_layer_emit_update ( VIK_LAYER(vtl) );
9000 /* PUT IN RIGHT PLACE!!! */
9001 if ( event->button == 3 && vtl->waypoint_rightclick )
9003 if ( vtl->wp_right_click_menu )
9004 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9005 if ( vtl->current_wp ) {
9006 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9007 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 );
9008 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9010 vtl->waypoint_rightclick = FALSE;
9015 /*** New track ****/
9017 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9024 GdkDrawable *drawable;
9030 * Draw specified pixmap
9032 static gboolean draw_sync ( gpointer data )
9034 draw_sync_t *ds = (draw_sync_t*) data;
9035 // Sometimes don't want to draw
9036 // normally because another update has taken precedent such as panning the display
9037 // which means this pixmap is no longer valid
9038 if ( ds->vtl->draw_sync_do ) {
9039 gdk_threads_enter();
9040 gdk_draw_drawable (ds->drawable,
9043 0, 0, 0, 0, -1, -1);
9044 ds->vtl->draw_sync_done = TRUE;
9045 gdk_threads_leave();
9051 static gchar* distance_string (gdouble distance)
9055 /* draw label with distance */
9056 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9057 switch (dist_units) {
9058 case VIK_UNITS_DISTANCE_MILES:
9059 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9060 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9061 } else if (distance < 1609.4) {
9062 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9064 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9068 // VIK_UNITS_DISTANCE_KILOMETRES
9069 if (distance >= 1000 && distance < 100000) {
9070 g_sprintf(str, "%3.2f km", distance/1000.0);
9071 } else if (distance < 1000) {
9072 g_sprintf(str, "%d m", (int)distance);
9074 g_sprintf(str, "%d km", (int)distance/1000);
9078 return g_strdup (str);
9082 * Actually set the message in statusbar
9084 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9086 // Only show elevation data when track has some elevation properties
9087 gchar str_gain_loss[64];
9088 str_gain_loss[0] = '\0';
9089 gchar str_last_step[64];
9090 str_last_step[0] = '\0';
9091 gchar *str_total = distance_string (distance);
9093 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9094 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9095 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9097 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9100 if ( last_step > 0 ) {
9101 gchar *tmp = distance_string (last_step);
9102 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9106 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9108 // Write with full gain/loss information
9109 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9110 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9112 g_free ( str_total );
9116 * Figure out what information should be set in the statusbar and then write it
9118 static void update_statusbar ( VikTrwLayer *vtl )
9120 // Get elevation data
9121 gdouble elev_gain, elev_loss;
9122 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9124 /* Find out actual distance of current track */
9125 gdouble distance = vik_track_get_length (vtl->current_track);
9127 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9131 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9133 /* if we haven't sync'ed yet, we don't have time to do more. */
9134 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9135 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9136 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9138 static GdkPixmap *pixmap = NULL;
9140 // Need to check in case window has been resized
9141 w1 = vik_viewport_get_width(vvp);
9142 h1 = vik_viewport_get_height(vvp);
9144 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9146 gdk_drawable_get_size (pixmap, &w2, &h2);
9147 if (w1 != w2 || h1 != h2) {
9148 g_object_unref ( G_OBJECT ( pixmap ) );
9149 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9152 // Reset to background
9153 gdk_draw_drawable (pixmap,
9154 vtl->current_track_newpoint_gc,
9155 vik_viewport_get_pixmap(vvp),
9156 0, 0, 0, 0, -1, -1);
9158 draw_sync_t *passalong;
9161 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9163 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9164 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9165 // thus when we come to reset to the background it would include what we have already drawn!!
9166 gdk_draw_line ( pixmap,
9167 vtl->current_track_newpoint_gc,
9168 x1, y1, event->x, event->y );
9169 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9171 /* Find out actual distance of current track */
9172 gdouble distance = vik_track_get_length (vtl->current_track);
9174 // Now add distance to where the pointer is //
9177 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9178 vik_coord_to_latlon ( &coord, &ll );
9179 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9180 distance = distance + last_step;
9182 // Get elevation data
9183 gdouble elev_gain, elev_loss;
9184 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9186 // Adjust elevation data (if available) for the current pointer position
9188 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9189 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9190 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9191 // Adjust elevation of last track point
9192 if ( elev_new > last_tpt->altitude )
9194 elev_gain += elev_new - last_tpt->altitude;
9197 elev_loss += last_tpt->altitude - elev_new;
9202 // Display of the distance 'tooltip' during track creation is controlled by a preference
9204 if ( a_vik_get_create_track_tooltip() ) {
9206 gchar *str = distance_string (distance);
9208 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9209 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9210 pango_layout_set_text (pl, str, -1);
9212 pango_layout_get_pixel_size ( pl, &wd, &hd );
9215 // offset from cursor a bit depending on font size
9219 // Create a background block to make the text easier to read over the background map
9220 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9221 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9222 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9224 g_object_unref ( G_OBJECT ( pl ) );
9225 g_object_unref ( G_OBJECT ( background_block_gc ) );
9229 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9230 passalong->vtl = vtl;
9231 passalong->pixmap = pixmap;
9232 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9233 passalong->gc = vtl->current_track_newpoint_gc;
9237 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9239 // Update statusbar with full gain/loss information
9240 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9242 // draw pixmap when we have time to
9243 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9244 vtl->draw_sync_done = FALSE;
9245 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9247 return VIK_LAYER_TOOL_ACK;
9250 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9252 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9253 vtl->current_track = NULL;
9254 vik_layer_emit_update ( VIK_LAYER(vtl) );
9256 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9258 if ( vtl->current_track->trackpoints )
9260 GList *last = g_list_last(vtl->current_track->trackpoints);
9261 g_free ( last->data );
9262 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9265 update_statusbar ( vtl );
9267 vik_layer_emit_update ( VIK_LAYER(vtl) );
9274 * Common function to handle trackpoint button requests on either a route or a track
9275 * . enables adding a point via normal click
9276 * . enables removal of last point via right click
9277 * . finishing of the track or route via double clicking
9279 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9283 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9286 if ( event->button == 2 ) {
9287 // As the display is panning, the new track pixmap is now invalid so don't draw it
9288 // otherwise this drawing done results in flickering back to an old image
9289 vtl->draw_sync_do = FALSE;
9293 if ( event->button == 3 )
9295 if ( !vtl->current_track )
9298 if ( vtl->current_track->trackpoints )
9300 GList *last = g_list_last(vtl->current_track->trackpoints);
9301 g_free ( last->data );
9302 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9304 vik_track_calculate_bounds ( vtl->current_track );
9305 update_statusbar ( vtl );
9307 vik_layer_emit_update ( VIK_LAYER(vtl) );
9311 if ( event->type == GDK_2BUTTON_PRESS )
9313 /* subtract last (duplicate from double click) tp then end */
9314 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9316 GList *last = g_list_last(vtl->current_track->trackpoints);
9317 g_free ( last->data );
9318 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9319 /* undo last, then end */
9320 vtl->current_track = NULL;
9322 vik_layer_emit_update ( VIK_LAYER(vtl) );
9326 tp = vik_trackpoint_new();
9327 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9329 /* snap to other TP */
9330 if ( event->state & GDK_CONTROL_MASK )
9332 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9334 tp->coord = other_tp->coord;
9337 tp->newsegment = FALSE;
9338 tp->has_timestamp = FALSE;
9341 if ( vtl->current_track ) {
9342 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9343 /* Auto attempt to get elevation from DEM data (if it's available) */
9344 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9347 vtl->ct_x1 = vtl->ct_x2;
9348 vtl->ct_y1 = vtl->ct_y2;
9349 vtl->ct_x2 = event->x;
9350 vtl->ct_y2 = event->y;
9352 vik_layer_emit_update ( VIK_LAYER(vtl) );
9356 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9358 // ----------------------------------------------------- if current is a route - switch to new track
9359 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9361 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9362 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9364 new_track_create_common ( vtl, name );
9370 return tool_new_track_or_route_click ( vtl, event, vvp );
9373 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9375 if ( event->button == 2 ) {
9376 // Pan moving ended - enable potential point drawing again
9377 vtl->draw_sync_do = TRUE;
9378 vtl->draw_sync_done = TRUE;
9382 /*** New route ****/
9384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9389 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9391 // -------------------------- if current is a track - switch to new route
9392 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9394 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9395 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9396 new_route_create_common ( vtl, name );
9402 return tool_new_track_or_route_click ( vtl, event, vvp );
9405 /*** New waypoint ****/
9407 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9412 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9415 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9417 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9418 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9419 trw_layer_calculate_bounds_waypoints ( vtl );
9420 vik_layer_emit_update ( VIK_LAYER(vtl) );
9426 /*** Edit trackpoint ****/
9428 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9430 tool_ed_t *t = g_new(tool_ed_t, 1);
9436 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9441 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9443 tool_ed_t *t = data;
9444 VikViewport *vvp = t->vvp;
9445 TPSearchParams params;
9446 /* OUTDATED DOCUMENTATION:
9447 find 5 pixel range on each side. then put these UTM, and a pointer
9448 to the winning track name (and maybe the winning track itself), and a
9449 pointer to the winning trackpoint, inside an array or struct. pass
9450 this along, do a foreach on the tracks which will do a foreach on the
9453 params.x = event->x;
9454 params.y = event->y;
9455 params.closest_track_id = NULL;
9456 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9457 params.closest_tp = NULL;
9458 params.closest_tpl = NULL;
9459 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9461 if ( event->button != 1 )
9464 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9467 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9470 if ( vtl->current_tpl )
9472 /* 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.) */
9473 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9474 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9479 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9481 if ( current_tr->visible &&
9482 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9483 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9484 marker_begin_move ( t, event->x, event->y );
9490 if ( vtl->tracks_visible )
9491 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9493 if ( params.closest_tp )
9495 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9496 vtl->current_tpl = params.closest_tpl;
9497 vtl->current_tp_id = params.closest_track_id;
9498 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9499 trw_layer_tpwin_init ( vtl );
9500 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9501 vik_layer_emit_update ( VIK_LAYER(vtl) );
9505 if ( vtl->routes_visible )
9506 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9508 if ( params.closest_tp )
9510 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9511 vtl->current_tpl = params.closest_tpl;
9512 vtl->current_tp_id = params.closest_track_id;
9513 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9514 trw_layer_tpwin_init ( vtl );
9515 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9516 vik_layer_emit_update ( VIK_LAYER(vtl) );
9520 /* these aren't the droids you're looking for */
9524 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9526 tool_ed_t *t = data;
9527 VikViewport *vvp = t->vvp;
9529 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9535 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9538 if ( event->state & GDK_CONTROL_MASK )
9540 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9541 if ( tp && tp != vtl->current_tpl->data )
9542 new_coord = tp->coord;
9544 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9547 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9548 marker_moveto ( t, x, y );
9556 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9558 tool_ed_t *t = data;
9559 VikViewport *vvp = t->vvp;
9561 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9563 if ( event->button != 1)
9568 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9571 if ( event->state & GDK_CONTROL_MASK )
9573 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9574 if ( tp && tp != vtl->current_tpl->data )
9575 new_coord = tp->coord;
9578 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9579 if ( vtl->current_tp_track )
9580 vik_track_calculate_bounds ( vtl->current_tp_track );
9582 marker_end_move ( t );
9584 /* diff dist is diff from orig */
9586 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9588 vik_layer_emit_update ( VIK_LAYER(vtl) );
9595 /*** Route Finder ***/
9596 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9601 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9604 if ( !vtl ) return FALSE;
9605 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9606 if ( event->button == 3 && vtl->route_finder_current_track ) {
9608 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9610 vtl->route_finder_coord = *new_end;
9612 vik_layer_emit_update ( VIK_LAYER(vtl) );
9613 /* remove last ' to:...' */
9614 if ( vtl->route_finder_current_track->comment ) {
9615 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9616 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9617 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9618 last_to - vtl->route_finder_current_track->comment - 1);
9619 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9624 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9625 struct LatLon start, end;
9627 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9628 vik_coord_to_latlon ( &(tmp), &end );
9629 vtl->route_finder_coord = tmp; /* for continuations */
9631 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9632 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9633 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9635 vtl->route_finder_check_added_track = TRUE;
9636 vtl->route_finder_started = FALSE;
9639 vik_routing_default_find ( vtl, start, end);
9641 /* see if anything was done -- a track was added or appended to */
9642 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9643 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 ) );
9644 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9645 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9646 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9647 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9650 if ( vtl->route_finder_added_track )
9651 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9653 vtl->route_finder_added_track = NULL;
9654 vtl->route_finder_check_added_track = FALSE;
9655 vtl->route_finder_append = FALSE;
9657 vik_layer_emit_update ( VIK_LAYER(vtl) );
9659 vtl->route_finder_started = TRUE;
9660 vtl->route_finder_coord = tmp;
9661 vtl->route_finder_current_track = NULL;
9666 /*** Show picture ****/
9668 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9673 /* Params are: vvp, event, last match found or NULL */
9674 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9676 if ( wp->image && wp->visible )
9678 gint x, y, slackx, slacky;
9679 GdkEventButton *event = (GdkEventButton *) params[1];
9681 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9682 slackx = wp->image_width / 2;
9683 slacky = wp->image_height / 2;
9684 if ( x <= event->x + slackx && x >= event->x - slackx
9685 && y <= event->y + slacky && y >= event->y - slacky )
9687 params[2] = wp->image; /* we've found a match. however continue searching
9688 * since we want to find the last match -- that
9689 * is, the match that was drawn last. */
9694 static void trw_layer_show_picture ( gpointer pass_along[6] )
9696 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9698 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9701 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9702 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9703 g_free ( quoted_file );
9704 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9706 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() );
9707 g_error_free ( err );
9710 #endif /* WINDOWS */
9713 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9715 gpointer params[3] = { vvp, event, NULL };
9716 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9718 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9721 static gpointer pass_along[6];
9722 pass_along[0] = vtl;
9723 pass_along[5] = params[2];
9724 trw_layer_show_picture ( pass_along );
9725 return TRUE; /* found a match */
9728 return FALSE; /* go through other layers, searching for a match */
9731 /***************************************************************************
9733 ***************************************************************************/
9736 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9738 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9739 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9742 /* Structure for thumbnail creating data used in the background thread */
9744 VikTrwLayer *vtl; // Layer needed for redrawing
9745 GSList *pics; // Image list
9746 } thumbnail_create_thread_data;
9748 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9750 guint total = g_slist_length(tctd->pics), done = 0;
9751 while ( tctd->pics )
9753 a_thumbnails_create ( (gchar *) tctd->pics->data );
9754 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9756 return -1; /* Abort thread */
9758 tctd->pics = tctd->pics->next;
9761 // Redraw to show the thumbnails as they are now created
9762 if ( IS_VIK_LAYER(tctd->vtl) )
9763 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9768 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9770 while ( tctd->pics )
9772 g_free ( tctd->pics->data );
9773 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9778 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9780 if ( ! vtl->has_verified_thumbnails )
9782 GSList *pics = NULL;
9783 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9786 gint len = g_slist_length ( pics );
9787 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9788 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9791 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9793 (vik_thr_func) create_thumbnails_thread,
9795 (vik_thr_free_func) thumbnail_create_thread_free,
9803 static const gchar* my_track_colors ( gint ii )
9805 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9817 // Fast and reliable way of returning a colour
9818 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9821 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9823 GHashTableIter iter;
9824 gpointer key, value;
9828 g_hash_table_iter_init ( &iter, vtl->tracks );
9830 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9832 // Tracks get a random spread of colours if not already assigned
9833 if ( ! VIK_TRACK(value)->has_color ) {
9834 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9835 VIK_TRACK(value)->color = vtl->track_color;
9837 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9839 VIK_TRACK(value)->has_color = TRUE;
9842 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9845 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9851 g_hash_table_iter_init ( &iter, vtl->routes );
9853 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9855 // Routes get an intermix of reds
9856 if ( ! VIK_TRACK(value)->has_color ) {
9858 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9860 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9861 VIK_TRACK(value)->has_color = TRUE;
9864 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9871 * (Re)Calculate the bounds of the waypoints in this layer,
9872 * This should be called whenever waypoints are changed
9874 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9876 struct LatLon topleft = { 0.0, 0.0 };
9877 struct LatLon bottomright = { 0.0, 0.0 };
9880 GHashTableIter iter;
9881 gpointer key, value;
9883 g_hash_table_iter_init ( &iter, vtl->waypoints );
9885 // Set bounds to first point
9886 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9887 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9888 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9891 // Ensure there is another point...
9892 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9894 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9896 // See if this point increases the bounds.
9897 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9899 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9900 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9901 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9902 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9906 vtl->waypoints_bbox.north = topleft.lat;
9907 vtl->waypoints_bbox.east = bottomright.lon;
9908 vtl->waypoints_bbox.south = bottomright.lat;
9909 vtl->waypoints_bbox.west = topleft.lon;
9912 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9914 vik_track_calculate_bounds ( trk );
9917 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9919 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9920 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9923 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9925 if ( ! VIK_LAYER(vtl)->vt )
9928 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9929 if ( g_hash_table_size (vtl->tracks) > 1 )
9930 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9932 if ( g_hash_table_size (vtl->routes) > 1 )
9933 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9935 if ( g_hash_table_size (vtl->waypoints) > 1 )
9936 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9939 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9941 if ( VIK_LAYER(vtl)->realized )
9942 trw_layer_verify_thumbnails ( vtl, vvp );
9943 trw_layer_track_alloc_colors ( vtl );
9945 trw_layer_calculate_bounds_waypoints ( vtl );
9946 trw_layer_calculate_bounds_tracks ( vtl );
9948 // Apply treeview sort after loading all the tracks for this layer
9949 // (rather than sorted insert on each individual track additional)
9950 // and after subsequent changes to the properties as the specified order may have changed.
9951 // since the sorting of a treeview section is now very quick
9952 // NB sorting is also performed after every name change as well to maintain the list order
9953 trw_layer_sort_all ( vtl );
9956 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9958 return vtl->coord_mode;
9962 * Uniquify the whole layer
9963 * Also requires the layers panel as the names shown there need updating too
9964 * Returns whether the operation was successful or not
9966 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9969 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9970 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9971 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9977 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9979 vik_coord_convert ( &(wp->coord), *dest_mode );
9982 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9984 vik_track_convert ( tr, *dest_mode );
9987 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9989 if ( vtl->coord_mode != dest_mode )
9991 vtl->coord_mode = dest_mode;
9992 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9993 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9994 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9998 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10000 vtl->menu_selection = selection;
10003 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10005 return (vtl->menu_selection);
10008 /* ----------- Downloading maps along tracks --------------- */
10010 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10012 /* TODO: calculating based on current size of viewport */
10013 const gdouble w_at_zoom_0_125 = 0.0013;
10014 const gdouble h_at_zoom_0_125 = 0.0011;
10015 gdouble zoom_factor = zoom_level/0.125;
10017 wh->lat = h_at_zoom_0_125 * zoom_factor;
10018 wh->lon = w_at_zoom_0_125 * zoom_factor;
10020 return 0; /* all OK */
10023 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10025 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10026 (dist->lat >= ABS(to->north_south - from->north_south)))
10029 VikCoord *coord = g_malloc(sizeof(VikCoord));
10030 coord->mode = VIK_COORD_LATLON;
10032 if (ABS(gradient) < 1) {
10033 if (from->east_west > to->east_west)
10034 coord->east_west = from->east_west - dist->lon;
10036 coord->east_west = from->east_west + dist->lon;
10037 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10039 if (from->north_south > to->north_south)
10040 coord->north_south = from->north_south - dist->lat;
10042 coord->north_south = from->north_south + dist->lat;
10043 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10049 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10051 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10052 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10054 VikCoord *next = from;
10056 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10058 list = g_list_prepend(list, next);
10064 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10066 typedef struct _Rect {
10071 #define GLRECT(iter) ((Rect *)((iter)->data))
10074 GList *rects_to_download = NULL;
10077 if (get_download_area_width(vvp, zoom_level, &wh))
10080 GList *iter = tr->trackpoints;
10084 gboolean new_map = TRUE;
10085 VikCoord *cur_coord, tl, br;
10088 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10090 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10091 rect = g_malloc(sizeof(Rect));
10094 rect->center = *cur_coord;
10095 rects_to_download = g_list_prepend(rects_to_download, rect);
10100 gboolean found = FALSE;
10101 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10102 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10113 GList *fillins = NULL;
10114 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10115 /* seems that ATM the function get_next_coord works only for LATLON */
10116 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10117 /* fill-ins for far apart points */
10118 GList *cur_rect, *next_rect;
10119 for (cur_rect = rects_to_download;
10120 (next_rect = cur_rect->next) != NULL;
10121 cur_rect = cur_rect->next) {
10122 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10123 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10124 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10128 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10131 GList *iter = fillins;
10133 cur_coord = (VikCoord *)(iter->data);
10134 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10135 rect = g_malloc(sizeof(Rect));
10138 rect->center = *cur_coord;
10139 rects_to_download = g_list_prepend(rects_to_download, rect);
10144 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10145 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10149 for (iter = fillins; iter; iter = iter->next)
10150 g_free(iter->data);
10151 g_list_free(fillins);
10153 if (rects_to_download) {
10154 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10155 g_free(rect_iter->data);
10156 g_list_free(rects_to_download);
10160 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10164 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10165 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10166 gint selected_zoom, default_zoom;
10168 VikTrwLayer *vtl = pass_along[0];
10169 VikLayersPanel *vlp = pass_along[1];
10171 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10172 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10174 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10178 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10180 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10181 int num_maps = g_list_length(vmls);
10184 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10188 // Convert from list of vmls to list of names. Allowing the user to select one of them
10189 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10190 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10192 gchar **np = map_names;
10193 VikMapsLayer **lp = map_layers;
10195 for (i = 0; i < num_maps; i++) {
10196 vml = (VikMapsLayer *)(vmls->data);
10198 *np++ = vik_maps_layer_get_map_label(vml);
10201 // Mark end of the array lists
10205 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10206 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10207 if (cur_zoom == zoom_vals[default_zoom])
10210 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10212 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10215 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10218 for (i = 0; i < num_maps; i++)
10219 g_free(map_names[i]);
10221 g_free(map_layers);
10227 /**** lowest waypoint number calculation ***/
10228 static gint highest_wp_number_name_to_number(const gchar *name) {
10229 if ( strlen(name) == 3 ) {
10230 int n = atoi(name);
10231 if ( n < 100 && name[0] != '0' )
10233 if ( n < 10 && name[0] != '0' )
10241 static void highest_wp_number_reset(VikTrwLayer *vtl)
10243 vtl->highest_wp_number = -1;
10246 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10248 /* if is bigger that top, add it */
10249 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10250 if ( new_wp_num > vtl->highest_wp_number )
10251 vtl->highest_wp_number = new_wp_num;
10254 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10256 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10257 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10258 if ( vtl->highest_wp_number == old_wp_num ) {
10260 vtl->highest_wp_number--;
10262 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10263 /* search down until we find something that *does* exist */
10265 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10266 vtl->highest_wp_number--;
10267 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10272 /* get lowest unused number */
10273 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10276 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10278 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10279 return g_strdup(buf);
10283 * trw_layer_create_track_list_both:
10285 * Create the latest list of tracks and routes
10287 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10289 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10290 GList *tracks = NULL;
10291 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10292 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10294 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10297 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] )
10299 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
10301 gchar *title = NULL;
10302 if ( GPOINTER_TO_INT(pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10303 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10305 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10307 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), pass_along[2], trw_layer_create_track_list, FALSE );
10311 static void trw_layer_track_list_dialog ( gpointer lav[2] )
10313 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10314 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10316 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10317 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10321 static void trw_layer_waypoint_list_dialog ( gpointer lav[2] )
10323 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10324 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10326 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10327 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );