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 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4975 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4976 vtl->route_finder_coord = last_coord;
4977 vtl->route_finder_current_track = track;
4978 vtl->route_finder_started = TRUE;
4980 if ( track->trackpoints )
4981 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4988 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4990 // If have a vlp then perform a basic test to see if any DEM info available...
4992 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
4994 if ( !g_list_length(dems) ) {
4995 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5003 * apply_dem_data_common:
5005 * A common function for applying the DEM values and reporting the results.
5007 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5009 if ( !trw_layer_dem_test ( vtl, vlp ) )
5012 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5013 // Inform user how much was changed
5015 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5016 g_snprintf(str, 64, tmp_str, changed);
5017 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5020 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
5022 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5024 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5025 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5027 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5030 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
5033 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
5035 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5037 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5038 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5040 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5043 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
5049 * A common function for applying the elevation smoothing and reporting the results.
5051 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5053 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5054 // Inform user how much was changed
5056 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5057 g_snprintf(str, 64, tmp_str, changed);
5058 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5064 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5066 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5068 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5069 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5071 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5076 smooth_it ( vtl, track, FALSE );
5079 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5081 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5083 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5084 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5086 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5091 smooth_it ( vtl, track, TRUE );
5095 * Commonal helper function
5097 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5100 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5101 g_snprintf(str, 64, tmp_str, changed);
5102 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5105 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5107 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5108 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5110 if ( !trw_layer_dem_test ( vtl, vlp ) )
5114 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5116 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5118 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5122 GHashTableIter iter;
5123 gpointer key, value;
5125 g_hash_table_iter_init ( &iter, vtl->waypoints );
5126 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5127 VikWaypoint *wp = VIK_WAYPOINT(value);
5128 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5131 wp_changed_message ( vtl, changed );
5134 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5136 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5137 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5139 if ( !trw_layer_dem_test ( vtl, vlp ) )
5143 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5145 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5147 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5151 GHashTableIter iter;
5152 gpointer key, value;
5154 g_hash_table_iter_init ( &iter, vtl->waypoints );
5155 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5156 VikWaypoint *wp = VIK_WAYPOINT(value);
5157 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5160 wp_changed_message ( vtl, changed );
5163 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5165 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5167 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5168 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5170 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5175 GList *trps = track->trackpoints;
5178 trps = g_list_last(trps);
5179 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5182 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5184 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5186 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5187 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5189 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5194 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5197 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5200 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5202 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5204 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5205 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5207 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5212 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5215 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5218 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5220 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5222 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5223 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5225 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5230 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5233 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5237 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5239 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5241 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5243 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5244 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5246 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5248 if ( trk && trk->trackpoints )
5250 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5251 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5252 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5253 if ( pass_along[1] )
5254 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5256 vik_layer_emit_update ( VIK_LAYER(vtl) );
5261 * Refine the selected track/route with a routing engine.
5262 * The routing engine is selected by the user, when requestiong the job.
5264 static void trw_layer_route_refine ( gpointer pass_along[6] )
5266 static gint last_engine = 0;
5267 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5270 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5271 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5273 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5275 if ( trk && trk->trackpoints )
5277 /* Check size of the route */
5278 int nb = vik_track_get_tp_count(trk);
5280 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5281 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5282 GTK_MESSAGE_WARNING,
5283 GTK_BUTTONS_OK_CANCEL,
5284 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5286 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5287 gtk_widget_destroy ( dialog );
5288 if (response != GTK_RESPONSE_OK )
5291 /* Select engine from dialog */
5292 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5293 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5294 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5296 GTK_RESPONSE_REJECT,
5298 GTK_RESPONSE_ACCEPT,
5300 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5301 gtk_widget_show_all(label);
5303 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5305 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5306 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5307 gtk_widget_show_all(combo);
5309 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5311 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5313 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5315 /* Dialog validated: retrieve selected engine and do the job */
5316 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5317 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5320 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5322 /* Force saving track */
5323 /* FIXME: remove or rename this hack */
5324 vtl->route_finder_check_added_track = TRUE;
5327 vik_routing_engine_refine (routing, vtl, trk);
5329 /* FIXME: remove or rename this hack */
5330 if ( vtl->route_finder_added_track )
5331 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5333 vtl->route_finder_added_track = NULL;
5334 vtl->route_finder_check_added_track = FALSE;
5336 vik_layer_emit_update ( VIK_LAYER(vtl) );
5338 /* Restore cursor */
5339 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5341 gtk_widget_destroy ( dialog );
5345 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5347 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5348 trw_layer_tpwin_init ( vtl );
5351 /*************************************
5352 * merge/split by time routines
5353 *************************************/
5355 /* called for each key in track hash table.
5356 * If the current track has the same time stamp type, add it to the result,
5357 * except the one pointed by "exclude".
5358 * set exclude to NULL if there is no exclude to check.
5359 * Note that the result is in reverse (for performance reasons).
5364 gboolean with_timestamps;
5366 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5368 twt_udata *user_data = udata;
5369 VikTrackpoint *p1, *p2;
5371 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5375 if (VIK_TRACK(value)->trackpoints) {
5376 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5377 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5379 if ( user_data->with_timestamps ) {
5380 if (!p1->has_timestamp || !p2->has_timestamp) {
5385 // Don't add tracks with timestamps when getting non timestamp tracks
5386 if (p1->has_timestamp || p2->has_timestamp) {
5392 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5395 /* called for each key in track hash table. if original track user_data[1] is close enough
5396 * to the passed one, add it to list in user_data[0]
5398 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5401 VikTrackpoint *p1, *p2;
5402 VikTrack *trk = VIK_TRACK(value);
5404 GList **nearby_tracks = ((gpointer *)user_data)[0];
5405 GList *tpoints = ((gpointer *)user_data)[1];
5408 * detect reasons for not merging, and return
5409 * if no reason is found not to merge, then do it.
5412 // Exclude the original track from the compiled list
5413 if (trk->trackpoints == tpoints) {
5417 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5418 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5420 if (trk->trackpoints) {
5421 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5422 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5424 if (!p1->has_timestamp || !p2->has_timestamp) {
5425 //g_print("no timestamp\n");
5429 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5430 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5431 if (! (abs(t1 - p2->timestamp) < threshold ||
5433 abs(p1->timestamp - t2) < threshold)
5440 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5443 /* comparison function used to sort tracks; a and b are hash table keys */
5444 /* Not actively used - can be restored if needed
5445 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5447 GHashTable *tracks = user_data;
5450 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5451 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5453 if (t1 < t2) return -1;
5454 if (t1 > t2) return 1;
5459 /* comparison function used to sort trackpoints */
5460 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5462 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5464 if (t1 < t2) return -1;
5465 if (t1 > t2) return 1;
5470 * comparison function which can be used to sort tracks or waypoints by name
5472 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5474 const gchar* namea = (const gchar*) a;
5475 const gchar* nameb = (const gchar*) b;
5476 if ( namea == NULL || nameb == NULL)
5479 // Same sort method as used in the vik_treeview_*_alphabetize functions
5480 return strcmp ( namea, nameb );
5484 * Attempt to merge selected track with other tracks specified by the user
5485 * Tracks to merge with must be of the same 'type' as the selected track -
5486 * either all with timestamps, or all without timestamps
5488 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5490 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5491 GList *other_tracks = NULL;
5492 GHashTable *ght_tracks;
5493 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5494 ght_tracks = vtl->routes;
5496 ght_tracks = vtl->tracks;
5498 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5503 if ( !track->trackpoints )
5507 udata.result = &other_tracks;
5508 udata.exclude = track->trackpoints;
5509 // Allow merging with 'similar' time type time tracks
5510 // i.e. either those times, or those without
5511 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5513 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5514 other_tracks = g_list_reverse(other_tracks);
5516 if ( !other_tracks ) {
5517 if ( udata.with_timestamps )
5518 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5520 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5524 // Sort alphabetically for user presentation
5525 // Convert into list of names for usage with dialog function
5526 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5527 GList *other_tracks_names = NULL;
5528 GList *iter = g_list_first ( other_tracks );
5530 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5531 iter = g_list_next ( iter );
5534 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5536 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5540 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5541 g_list_free(other_tracks);
5542 g_list_free(other_tracks_names);
5547 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5548 VikTrack *merge_track;
5549 if ( track->is_route )
5550 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5552 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5555 vik_track_steal_and_append_trackpoints ( track, merge_track );
5556 if ( track->is_route )
5557 vik_trw_layer_delete_route (vtl, merge_track);
5559 vik_trw_layer_delete_track (vtl, merge_track);
5560 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5563 for (l = merge_list; l != NULL; l = g_list_next(l))
5565 g_list_free(merge_list);
5567 vik_layer_emit_update( VIK_LAYER(vtl) );
5571 // c.f. trw_layer_sorted_track_id_by_name_list
5572 // but don't add the specified track to the list (normally current track)
5573 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5575 twt_udata *user_data = udata;
5578 if (trk->trackpoints == user_data->exclude) {
5582 // Sort named list alphabetically
5583 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5587 * Join - this allows combining 'tracks' and 'track routes'
5588 * i.e. doesn't care about whether tracks have consistent timestamps
5589 * ATM can only append one track at a time to the currently selected track
5591 static void trw_layer_append_track ( gpointer pass_along[6] )
5594 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5596 GHashTable *ght_tracks;
5597 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5598 ght_tracks = vtl->routes;
5600 ght_tracks = vtl->tracks;
5602 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5607 GList *other_tracks_names = NULL;
5609 // Sort alphabetically for user presentation
5610 // Convert into list of names for usage with dialog function
5611 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5613 udata.result = &other_tracks_names;
5614 udata.exclude = trk->trackpoints;
5616 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5618 // Note the limit to selecting one track only
5619 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5620 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5621 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5624 trk->is_route ? _("Append Route"): _("Append Track"),
5625 trk->is_route ? _("Select the route to append after the current route") :
5626 _("Select the track to append after the current track") );
5628 g_list_free(other_tracks_names);
5630 // It's a list, but shouldn't contain more than one other track!
5631 if ( append_list ) {
5633 for (l = append_list; l != NULL; l = g_list_next(l)) {
5634 // TODO: at present this uses the first track found by name,
5635 // which with potential multiple same named tracks may not be the one selected...
5636 VikTrack *append_track;
5637 if ( trk->is_route )
5638 append_track = vik_trw_layer_get_route ( vtl, l->data );
5640 append_track = vik_trw_layer_get_track ( vtl, l->data );
5642 if ( append_track ) {
5643 vik_track_steal_and_append_trackpoints ( trk, append_track );
5644 if ( trk->is_route )
5645 vik_trw_layer_delete_route (vtl, append_track);
5647 vik_trw_layer_delete_track (vtl, append_track);
5650 for (l = append_list; l != NULL; l = g_list_next(l))
5652 g_list_free(append_list);
5654 vik_layer_emit_update( VIK_LAYER(vtl) );
5659 * Very similar to trw_layer_append_track for joining
5660 * but this allows selection from the 'other' list
5661 * If a track is selected, then is shows routes and joins the selected one
5662 * If a route is selected, then is shows tracks and joins the selected one
5664 static void trw_layer_append_other ( gpointer pass_along[6] )
5667 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5669 GHashTable *ght_mykind, *ght_others;
5670 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5671 ght_mykind = vtl->routes;
5672 ght_others = vtl->tracks;
5675 ght_mykind = vtl->tracks;
5676 ght_others = vtl->routes;
5679 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5684 GList *other_tracks_names = NULL;
5686 // Sort alphabetically for user presentation
5687 // Convert into list of names for usage with dialog function
5688 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5690 udata.result = &other_tracks_names;
5691 udata.exclude = trk->trackpoints;
5693 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5695 // Note the limit to selecting one track only
5696 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5697 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5698 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5701 trk->is_route ? _("Append Track"): _("Append Route"),
5702 trk->is_route ? _("Select the track to append after the current route") :
5703 _("Select the route to append after the current track") );
5705 g_list_free(other_tracks_names);
5707 // It's a list, but shouldn't contain more than one other track!
5708 if ( append_list ) {
5710 for (l = append_list; l != NULL; l = g_list_next(l)) {
5711 // TODO: at present this uses the first track found by name,
5712 // which with potential multiple same named tracks may not be the one selected...
5714 // Get FROM THE OTHER TYPE list
5715 VikTrack *append_track;
5716 if ( trk->is_route )
5717 append_track = vik_trw_layer_get_track ( vtl, l->data );
5719 append_track = vik_trw_layer_get_route ( vtl, l->data );
5721 if ( append_track ) {
5723 if ( !append_track->is_route &&
5724 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5725 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5727 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5728 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5729 vik_track_merge_segments ( append_track );
5730 vik_track_to_routepoints ( append_track );
5737 vik_track_steal_and_append_trackpoints ( trk, append_track );
5739 // Delete copied which is FROM THE OTHER TYPE list
5740 if ( trk->is_route )
5741 vik_trw_layer_delete_track (vtl, append_track);
5743 vik_trw_layer_delete_route (vtl, append_track);
5746 for (l = append_list; l != NULL; l = g_list_next(l))
5748 g_list_free(append_list);
5749 vik_layer_emit_update( VIK_LAYER(vtl) );
5753 /* merge by segments */
5754 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5756 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5757 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5758 guint segments = vik_track_merge_segments ( trk );
5759 // NB currently no need to redraw as segments not actually shown on the display
5760 // However inform the user of what happened:
5762 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5763 g_snprintf(str, 64, tmp_str, segments);
5764 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5767 /* merge by time routine */
5768 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5770 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5774 GList *tracks_with_timestamp = NULL;
5775 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5776 if (orig_trk->trackpoints &&
5777 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5778 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5783 udata.result = &tracks_with_timestamp;
5784 udata.exclude = orig_trk->trackpoints;
5785 udata.with_timestamps = TRUE;
5786 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5787 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5789 if (!tracks_with_timestamp) {
5790 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5793 g_list_free(tracks_with_timestamp);
5795 static guint threshold_in_minutes = 1;
5796 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5797 _("Merge Threshold..."),
5798 _("Merge when time between tracks less than:"),
5799 &threshold_in_minutes)) {
5803 // keep attempting to merge all tracks until no merges within the time specified is possible
5804 gboolean attempt_merge = TRUE;
5805 GList *nearby_tracks = NULL;
5807 static gpointer params[3];
5809 while ( attempt_merge ) {
5811 // Don't try again unless tracks have changed
5812 attempt_merge = FALSE;
5814 trps = orig_trk->trackpoints;
5818 if (nearby_tracks) {
5819 g_list_free(nearby_tracks);
5820 nearby_tracks = NULL;
5823 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5824 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5826 /* g_print("Original track times: %d and %d\n", t1, t2); */
5827 params[0] = &nearby_tracks;
5828 params[1] = (gpointer)trps;
5829 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5831 /* get a list of adjacent-in-time tracks */
5832 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5835 GList *l = nearby_tracks;
5838 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5839 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5841 t1 = get_first_trackpoint(l)->timestamp;
5842 t2 = get_last_trackpoint(l)->timestamp;
5843 #undef get_first_trackpoint
5844 #undef get_last_trackpoint
5845 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5848 /* remove trackpoints from merged track, delete track */
5849 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5850 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5852 // Tracks have changed, therefore retry again against all the remaining tracks
5853 attempt_merge = TRUE;
5858 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5861 g_list_free(nearby_tracks);
5863 vik_layer_emit_update( VIK_LAYER(vtl) );
5867 * Split a track at the currently selected trackpoint
5869 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5871 if ( !vtl->current_tpl )
5874 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5875 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5877 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5878 GList *newglist = g_list_alloc ();
5879 newglist->prev = NULL;
5880 newglist->next = vtl->current_tpl->next;
5881 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5882 tr->trackpoints = newglist;
5884 vtl->current_tpl->next->prev = newglist; /* end old track here */
5885 vtl->current_tpl->next = NULL;
5887 // Bounds of the selected track changed due to the split
5888 vik_track_calculate_bounds ( vtl->current_tp_track );
5890 vtl->current_tpl = newglist; /* change tp to first of new track. */
5891 vtl->current_tp_track = tr;
5894 vik_trw_layer_add_route ( vtl, name, tr );
5896 vik_trw_layer_add_track ( vtl, name, tr );
5898 // Bounds of the new track created by the split
5899 vik_track_calculate_bounds ( tr );
5905 // Also need id of newly created track
5908 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5910 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5912 if ( trkf && udata.uuid )
5913 vtl->current_tp_id = udata.uuid;
5915 vtl->current_tp_id = NULL;
5917 vik_layer_emit_update(VIK_LAYER(vtl));
5923 /* split by time routine */
5924 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5926 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5927 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5928 GList *trps = track->trackpoints;
5930 GList *newlists = NULL;
5931 GList *newtps = NULL;
5932 static guint thr = 1;
5939 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5940 _("Split Threshold..."),
5941 _("Split when time between trackpoints exceeds:"),
5946 /* iterate through trackpoints, and copy them into new lists without touching original list */
5947 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5951 ts = VIK_TRACKPOINT(iter->data)->timestamp;
5953 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5956 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5957 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5958 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5960 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5965 if (ts - prev_ts > thr*60) {
5966 /* flush accumulated trackpoints into new list */
5967 newlists = g_list_append(newlists, g_list_reverse(newtps));
5971 /* accumulate trackpoint copies in newtps, in reverse order */
5972 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5974 iter = g_list_next(iter);
5977 newlists = g_list_append(newlists, g_list_reverse(newtps));
5980 /* put lists of trackpoints into tracks */
5982 // Only bother updating if the split results in new tracks
5983 if (g_list_length (newlists) > 1) {
5988 tr = vik_track_copy ( track, FALSE );
5989 tr->trackpoints = (GList *)(iter->data);
5991 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5992 vik_trw_layer_add_track(vtl, new_tr_name, tr);
5993 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5994 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5995 g_free ( new_tr_name );
5996 vik_track_calculate_bounds ( tr );
5997 iter = g_list_next(iter);
5999 // Remove original track and then update the display
6000 vik_trw_layer_delete_track (vtl, track);
6001 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6003 g_list_free(newlists);
6007 * Split a track by the number of points as specified by the user
6009 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
6011 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6013 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6014 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6016 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6021 // Check valid track
6022 GList *trps = track->trackpoints;
6026 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6027 _("Split Every Nth Point"),
6028 _("Split on every Nth point:"),
6029 250, // Default value as per typical limited track capacity of various GPS devices
6033 // Was a valid number returned?
6039 GList *newlists = NULL;
6040 GList *newtps = NULL;
6045 /* accumulate trackpoint copies in newtps, in reverse order */
6046 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6048 if (count >= points) {
6049 /* flush accumulated trackpoints into new list */
6050 newlists = g_list_append(newlists, g_list_reverse(newtps));
6054 iter = g_list_next(iter);
6057 // If there is a remaining chunk put that into the new split list
6058 // This may well be the whole track if no split points were encountered
6060 newlists = g_list_append(newlists, g_list_reverse(newtps));
6063 /* put lists of trackpoints into tracks */
6065 // Only bother updating if the split results in new tracks
6066 if (g_list_length (newlists) > 1) {
6071 tr = vik_track_copy ( track, FALSE );
6072 tr->trackpoints = (GList *)(iter->data);
6074 if ( track->is_route ) {
6075 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6076 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6079 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6080 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6082 g_free ( new_tr_name );
6083 vik_track_calculate_bounds ( tr );
6085 iter = g_list_next(iter);
6087 // Remove original track and then update the display
6088 if ( track->is_route )
6089 vik_trw_layer_delete_route (vtl, track);
6091 vik_trw_layer_delete_track (vtl, track);
6092 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6094 g_list_free(newlists);
6098 * Split a track at the currently selected trackpoint
6100 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6102 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6103 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6104 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6108 * Split a track by its segments
6109 * Routes do not have segments so don't call this for routes
6111 static void trw_layer_split_segments ( gpointer pass_along[6] )
6113 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6114 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6121 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6124 for ( i = 0; i < ntracks; i++ ) {
6126 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6127 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6128 g_free ( new_tr_name );
6133 // Remove original track
6134 vik_trw_layer_delete_track ( vtl, trk );
6135 vik_layer_emit_update ( VIK_LAYER(vtl) );
6138 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6141 /* end of split/merge routines */
6143 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6147 // Find available adjacent trackpoint
6148 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6149 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6150 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6152 // Delete current trackpoint
6153 vik_trackpoint_free ( vtl->current_tpl->data );
6154 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6156 // Set to current to the available adjacent trackpoint
6157 vtl->current_tpl = new_tpl;
6159 if ( vtl->current_tp_track ) {
6160 vik_track_calculate_bounds ( vtl->current_tp_track );
6164 // Delete current trackpoint
6165 vik_trackpoint_free ( vtl->current_tpl->data );
6166 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6167 trw_layer_cancel_current_tp ( vtl, FALSE );
6172 * Delete the selected point
6174 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6176 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6178 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6179 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6181 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6186 if ( !vtl->current_tpl )
6189 trw_layer_trackpoint_selected_delete ( vtl, trk );
6191 // Track has been updated so update tps:
6192 trw_layer_cancel_tps_of_track ( vtl, trk );
6194 vik_layer_emit_update ( VIK_LAYER(vtl) );
6198 * Delete adjacent track points at the same position
6199 * AKA Delete Dulplicates on the Properties Window
6201 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6203 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6205 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6206 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6208 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6213 gulong removed = vik_track_remove_dup_points ( trk );
6215 // Track has been updated so update tps:
6216 trw_layer_cancel_tps_of_track ( vtl, trk );
6218 // Inform user how much was deleted as it's not obvious from the normal view
6220 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6221 g_snprintf(str, 64, tmp_str, removed);
6222 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6224 vik_layer_emit_update ( VIK_LAYER(vtl) );
6228 * Delete adjacent track points with the same timestamp
6229 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6231 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6233 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6235 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6236 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6238 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6243 gulong removed = vik_track_remove_same_time_points ( trk );
6245 // Track has been updated so update tps:
6246 trw_layer_cancel_tps_of_track ( vtl, trk );
6248 // Inform user how much was deleted as it's not obvious from the normal view
6250 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6251 g_snprintf(str, 64, tmp_str, removed);
6252 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6254 vik_layer_emit_update ( VIK_LAYER(vtl) );
6260 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6262 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6264 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6265 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6267 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6272 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6274 vik_layer_emit_update ( VIK_LAYER(vtl) );
6277 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6279 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6281 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6282 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6284 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6289 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6291 vik_layer_emit_update ( VIK_LAYER(vtl) );
6297 static void trw_layer_reverse ( gpointer pass_along[6] )
6299 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6301 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6302 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6304 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6309 vik_track_reverse ( track );
6311 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6315 * Similar to trw_layer_enum_item, but this uses a sorted method
6318 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6320 GList **list = (GList**)udata;
6321 // *list = g_list_prepend(*all, key); //unsorted method
6322 // Sort named list alphabetically
6323 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6328 * Now Waypoint specific sort
6330 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6332 GList **list = (GList**)udata;
6333 // Sort named list alphabetically
6334 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6338 * Track specific sort
6340 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6342 GList **list = (GList**)udata;
6343 // Sort named list alphabetically
6344 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6349 gboolean has_same_track_name;
6350 const gchar *same_track_name;
6351 } same_track_name_udata;
6353 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6355 const gchar* namea = (const gchar*) aa;
6356 const gchar* nameb = (const gchar*) bb;
6359 gint result = strcmp ( namea, nameb );
6361 if ( result == 0 ) {
6362 // Found two names the same
6363 same_track_name_udata *user_data = udata;
6364 user_data->has_same_track_name = TRUE;
6365 user_data->same_track_name = namea;
6368 // Leave ordering the same
6373 * Find out if any tracks have the same name in this hash table
6375 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6377 // Sort items by name, then compare if any next to each other are the same
6379 GList *track_names = NULL;
6380 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6383 if ( ! track_names )
6386 same_track_name_udata udata;
6387 udata.has_same_track_name = FALSE;
6389 // Use sort routine to traverse list comparing items
6390 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6391 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6392 // Still no tracks...
6396 return udata.has_same_track_name;
6400 * Force unqiue track names for the track table specified
6401 * Note the panel is a required parameter to enable the update of the names displayed
6402 * Specify if on tracks or else on routes
6404 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6406 // . Search list for an instance of repeated name
6407 // . get track of this name
6408 // . create new name
6409 // . rename track & update equiv. treeview iter
6410 // . repeat until all different
6412 same_track_name_udata udata;
6414 GList *track_names = NULL;
6415 udata.has_same_track_name = FALSE;
6416 udata.same_track_name = NULL;
6418 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6421 if ( ! track_names )
6424 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6426 // Still no tracks...
6427 if ( ! dummy_list1 )
6430 while ( udata.has_same_track_name ) {
6432 // Find a track with the same name
6435 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6437 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6441 g_critical("Houston, we've had a problem.");
6442 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6443 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6448 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6449 vik_track_set_name ( trk, newname );
6455 // Need want key of it for treeview update
6456 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6458 if ( trkf && udataU.uuid ) {
6462 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6464 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6467 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6469 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6471 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6475 // Start trying to find same names again...
6477 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6478 udata.has_same_track_name = FALSE;
6479 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6481 // No tracks any more - give up searching
6482 if ( ! dummy_list2 )
6483 udata.has_same_track_name = FALSE;
6487 vik_layers_panel_emit_update ( vlp );
6490 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6492 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6495 switch (GPOINTER_TO_INT (pass_along[2])) {
6496 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6497 iter = &(vtl->tracks_iter);
6498 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6500 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6501 iter = &(vtl->routes_iter);
6502 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6504 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6505 iter = &(vtl->waypoints_iter);
6506 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6510 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6513 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6515 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6518 switch (GPOINTER_TO_INT (pass_along[2])) {
6519 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6520 iter = &(vtl->tracks_iter);
6521 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6523 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6524 iter = &(vtl->routes_iter);
6525 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6527 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6528 iter = &(vtl->waypoints_iter);
6529 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6533 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6539 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6541 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6544 // Ensure list of track names offered is unique
6545 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6546 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6547 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6548 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6554 // Sort list alphabetically for better presentation
6555 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6558 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6562 // Get list of items to delete from the user
6563 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6566 _("Delete Selection"),
6567 _("Select tracks to delete"));
6570 // Delete requested tracks
6571 // since specificly requested, IMHO no need for extra confirmation
6572 if ( delete_list ) {
6574 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6575 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6576 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6578 g_list_free(delete_list);
6579 vik_layer_emit_update( VIK_LAYER(vtl) );
6586 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6588 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6591 // Ensure list of track names offered is unique
6592 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6593 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6594 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6595 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6601 // Sort list alphabetically for better presentation
6602 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6605 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6609 // Get list of items to delete from the user
6610 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6613 _("Delete Selection"),
6614 _("Select routes to delete") );
6617 // Delete requested routes
6618 // since specificly requested, IMHO no need for extra confirmation
6619 if ( delete_list ) {
6621 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6622 // This deletes first route it finds of that name (but uniqueness is enforced above)
6623 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6625 g_list_free(delete_list);
6626 vik_layer_emit_update( VIK_LAYER(vtl) );
6631 gboolean has_same_waypoint_name;
6632 const gchar *same_waypoint_name;
6633 } same_waypoint_name_udata;
6635 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6637 const gchar* namea = (const gchar*) aa;
6638 const gchar* nameb = (const gchar*) bb;
6641 gint result = strcmp ( namea, nameb );
6643 if ( result == 0 ) {
6644 // Found two names the same
6645 same_waypoint_name_udata *user_data = udata;
6646 user_data->has_same_waypoint_name = TRUE;
6647 user_data->same_waypoint_name = namea;
6650 // Leave ordering the same
6655 * Find out if any waypoints have the same name in this layer
6657 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6659 // Sort items by name, then compare if any next to each other are the same
6661 GList *waypoint_names = NULL;
6662 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6665 if ( ! waypoint_names )
6668 same_waypoint_name_udata udata;
6669 udata.has_same_waypoint_name = FALSE;
6671 // Use sort routine to traverse list comparing items
6672 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6673 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6674 // Still no waypoints...
6678 return udata.has_same_waypoint_name;
6682 * Force unqiue waypoint names for this layer
6683 * Note the panel is a required parameter to enable the update of the names displayed
6685 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6687 // . Search list for an instance of repeated name
6688 // . get waypoint of this name
6689 // . create new name
6690 // . rename waypoint & update equiv. treeview iter
6691 // . repeat until all different
6693 same_waypoint_name_udata udata;
6695 GList *waypoint_names = NULL;
6696 udata.has_same_waypoint_name = FALSE;
6697 udata.same_waypoint_name = NULL;
6699 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6702 if ( ! waypoint_names )
6705 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6707 // Still no waypoints...
6708 if ( ! dummy_list1 )
6711 while ( udata.has_same_waypoint_name ) {
6713 // Find a waypoint with the same name
6714 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6718 g_critical("Houston, we've had a problem.");
6719 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6720 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6725 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6727 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6729 // Start trying to find same names again...
6730 waypoint_names = NULL;
6731 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6732 udata.has_same_waypoint_name = FALSE;
6733 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6735 // No waypoints any more - give up searching
6736 if ( ! dummy_list2 )
6737 udata.has_same_waypoint_name = FALSE;
6741 vik_layers_panel_emit_update ( vlp );
6747 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6749 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6752 // Ensure list of waypoint names offered is unique
6753 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6754 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6755 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6756 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6762 // Sort list alphabetically for better presentation
6763 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6765 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6769 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6771 // Get list of items to delete from the user
6772 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6775 _("Delete Selection"),
6776 _("Select waypoints to delete"));
6779 // Delete requested waypoints
6780 // since specificly requested, IMHO no need for extra confirmation
6781 if ( delete_list ) {
6783 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6784 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6785 trw_layer_delete_waypoint_by_name (vtl, l->data);
6787 g_list_free(delete_list);
6789 trw_layer_calculate_bounds_waypoints ( vtl );
6790 vik_layer_emit_update( VIK_LAYER(vtl) );
6798 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6800 vik_treeview_item_toggle_visible ( vt, it );
6806 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6808 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6814 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6816 wp->visible = GPOINTER_TO_INT (on_off);
6822 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6824 wp->visible = !wp->visible;
6830 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6832 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6833 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6834 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6835 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6837 vik_layer_emit_update ( VIK_LAYER(vtl) );
6843 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6845 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6846 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6847 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6848 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6850 vik_layer_emit_update ( VIK_LAYER(vtl) );
6856 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6858 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6859 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6860 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6862 vik_layer_emit_update ( VIK_LAYER(vtl) );
6868 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6870 trk->visible = GPOINTER_TO_INT (on_off);
6876 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6878 trk->visible = !trk->visible;
6884 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6886 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6887 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6888 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6889 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6891 vik_layer_emit_update ( VIK_LAYER(vtl) );
6897 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6899 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6900 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6901 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6902 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6904 vik_layer_emit_update ( VIK_LAYER(vtl) );
6910 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6912 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6913 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6914 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6916 vik_layer_emit_update ( VIK_LAYER(vtl) );
6922 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6924 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6925 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6926 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6927 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6929 vik_layer_emit_update ( VIK_LAYER(vtl) );
6935 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6937 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6938 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6939 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6940 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6942 vik_layer_emit_update ( VIK_LAYER(vtl) );
6948 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6950 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6951 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6952 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6954 vik_layer_emit_update ( VIK_LAYER(vtl) );
6958 * vik_trw_layer_build_waypoint_list_t:
6960 * Helper function to construct a list of #vik_trw_waypoint_list_t
6962 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
6964 GList *waypoints_and_layers = NULL;
6965 // build waypoints_and_layers list
6966 while ( waypoints ) {
6967 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
6968 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
6970 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
6971 waypoints = g_list_next ( waypoints );
6973 return waypoints_and_layers;
6977 * trw_layer_create_waypoint_list:
6979 * Create the latest list of waypoints with the associated layer(s)
6980 * Although this will always be from a single layer here
6982 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
6984 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6985 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
6987 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
6991 * trw_layer_analyse_close:
6993 * Stuff to do on dialog closure
6995 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6997 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6998 gtk_widget_destroy ( dialog );
6999 vtl->tracks_analysis_dialog = NULL;
7003 * vik_trw_layer_build_track_list_t:
7005 * Helper function to construct a list of #vik_trw_track_list_t
7007 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7009 GList *tracks_and_layers = NULL;
7010 // build tracks_and_layers list
7012 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7013 vtdl->trk = VIK_TRACK(tracks->data);
7015 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7016 tracks = g_list_next ( tracks );
7018 return tracks_and_layers;
7022 * trw_layer_create_track_list:
7024 * Create the latest list of tracks with the associated layer(s)
7025 * Although this will always be from a single layer here
7027 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7029 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7030 GList *tracks = NULL;
7031 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7032 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7034 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7036 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7039 static void trw_layer_tracks_stats ( gpointer lav[2] )
7041 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7042 // There can only be one!
7043 if ( vtl->tracks_analysis_dialog )
7046 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7047 VIK_LAYER(vtl)->name,
7049 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7050 trw_layer_create_track_list,
7051 trw_layer_analyse_close );
7057 static void trw_layer_routes_stats ( gpointer lav[2] )
7059 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7060 // There can only be one!
7061 if ( vtl->tracks_analysis_dialog )
7064 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7065 VIK_LAYER(vtl)->name,
7067 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7068 trw_layer_create_track_list,
7069 trw_layer_analyse_close );
7072 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
7074 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7076 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
7079 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
7081 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7084 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7085 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7089 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
7091 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7094 if ( !strncmp(wp->comment, "http", 4) ) {
7095 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7096 } else if ( !strncmp(wp->description, "http", 4) ) {
7097 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7101 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7103 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7105 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7107 // No actual change to the name supplied
7109 if (strcmp(newname, wp->name) == 0 )
7112 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7115 // An existing waypoint has been found with the requested name
7116 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7117 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7122 // Update WP name and refresh the treeview
7123 vik_waypoint_set_name (wp, newname);
7125 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7126 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7128 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7133 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7135 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7137 // No actual change to the name supplied
7139 if (strcmp(newname, trk->name) == 0)
7142 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7145 // An existing track has been found with the requested name
7146 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7147 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7151 // Update track name and refresh GUI parts
7152 vik_track_set_name (trk, newname);
7154 // Update any subwindows that could be displaying this track which has changed name
7155 // Only one Track Edit Window
7156 if ( l->current_tp_track == trk && l->tpwin ) {
7157 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7159 // Property Dialog of the track
7160 vik_trw_layer_propwin_update ( trk );
7162 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7163 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7165 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7170 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7172 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7174 // No actual change to the name supplied
7176 if (strcmp(newname, trk->name) == 0)
7179 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7182 // An existing track has been found with the requested name
7183 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7184 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7188 // Update track name and refresh GUI parts
7189 vik_track_set_name (trk, newname);
7191 // Update any subwindows that could be displaying this track which has changed name
7192 // Only one Track Edit Window
7193 if ( l->current_tp_track == trk && l->tpwin ) {
7194 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7196 // Property Dialog of the track
7197 vik_trw_layer_propwin_update ( trk );
7199 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7200 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7202 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7209 static gboolean is_valid_geocache_name ( gchar *str )
7211 gint len = strlen ( str );
7212 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]));
7215 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7217 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7218 a_acquire_set_filter_track ( trk );
7221 #ifdef VIK_CONFIG_GOOGLE
7222 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7224 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7225 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7228 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7230 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7232 gchar *escaped = uri_escape ( tr->comment );
7233 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7234 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7241 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7242 /* viewpoint is now available instead */
7243 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7245 static gpointer pass_along[8];
7247 gboolean rv = FALSE;
7250 pass_along[1] = vlp;
7251 pass_along[2] = GINT_TO_POINTER (subtype);
7252 pass_along[3] = sublayer;
7253 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7254 pass_along[5] = vvp;
7255 pass_along[6] = iter;
7256 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7258 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7262 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7263 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7264 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7265 gtk_widget_show ( item );
7267 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7268 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7269 if (tr && tr->property_dialog)
7270 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7272 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7273 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7274 if (tr && tr->property_dialog)
7275 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7278 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7280 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7281 gtk_widget_show ( item );
7283 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7284 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7285 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7286 gtk_widget_show ( item );
7288 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7289 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7290 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7291 gtk_widget_show ( item );
7293 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7295 // Always create separator as now there is always at least the transform menu option
7296 item = gtk_menu_item_new ();
7297 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7298 gtk_widget_show ( item );
7300 /* could be a right-click using the tool */
7301 if ( vlp != NULL ) {
7302 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7303 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7305 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7306 gtk_widget_show ( item );
7309 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7311 if ( wp && wp->name ) {
7312 if ( is_valid_geocache_name ( wp->name ) ) {
7313 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7314 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7315 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7316 gtk_widget_show ( item );
7318 #ifdef VIK_CONFIG_GEOTAG
7319 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7320 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7321 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7322 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7323 gtk_widget_show ( item );
7327 if ( wp && wp->image )
7329 // Set up image paramater
7330 pass_along[5] = wp->image;
7332 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7333 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
7334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7336 gtk_widget_show ( item );
7338 #ifdef VIK_CONFIG_GEOTAG
7339 GtkWidget *geotag_submenu = gtk_menu_new ();
7340 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7342 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7343 gtk_widget_show ( item );
7344 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7346 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7348 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7349 gtk_widget_show ( item );
7351 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7353 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7354 gtk_widget_show ( item );
7360 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7361 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7362 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7363 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7365 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7366 gtk_widget_show ( item );
7372 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7373 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7374 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7375 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7376 gtk_widget_show ( item );
7377 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7378 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7379 gtk_widget_set_sensitive ( item, TRUE );
7381 gtk_widget_set_sensitive ( item, FALSE );
7384 item = gtk_menu_item_new ();
7385 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7386 gtk_widget_show ( item );
7389 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7392 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7395 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7396 gtk_widget_show ( item );
7399 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7401 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7402 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7403 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7404 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7405 gtk_widget_show ( item );
7407 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7408 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7409 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7410 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7411 gtk_widget_show ( item );
7413 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7414 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7415 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7416 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7417 gtk_widget_show ( item );
7419 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7420 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7421 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7422 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7423 gtk_widget_show ( item );
7425 GtkWidget *vis_submenu = gtk_menu_new ();
7426 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7427 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7428 gtk_widget_show ( item );
7429 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7431 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7432 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7433 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7434 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7435 gtk_widget_show ( item );
7437 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7438 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7439 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7440 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7441 gtk_widget_show ( item );
7443 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7444 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7446 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7447 gtk_widget_show ( item );
7449 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7450 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7451 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7452 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7455 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7459 if ( l->current_track && !l->current_track->is_route ) {
7460 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7461 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7462 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7463 gtk_widget_show ( item );
7465 item = gtk_menu_item_new ();
7466 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7467 gtk_widget_show ( item );
7470 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7471 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7472 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7473 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7474 gtk_widget_show ( item );
7476 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7477 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7478 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7479 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7480 gtk_widget_show ( item );
7481 // Make it available only when a new track *not* already in progress
7482 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7484 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7485 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7486 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7487 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7488 gtk_widget_show ( item );
7490 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7491 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7493 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7494 gtk_widget_show ( item );
7496 GtkWidget *vis_submenu = gtk_menu_new ();
7497 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7498 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7499 gtk_widget_show ( item );
7500 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7502 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7505 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7506 gtk_widget_show ( item );
7508 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7509 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7510 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7511 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7512 gtk_widget_show ( item );
7514 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7515 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7516 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7517 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7519 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7520 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7521 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7522 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7523 gtk_widget_show ( item );
7525 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7526 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7527 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7528 gtk_widget_show ( item );
7531 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7535 if ( l->current_track && l->current_track->is_route ) {
7536 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7537 // Reuse finish track method
7538 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7539 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7540 gtk_widget_show ( item );
7542 item = gtk_menu_item_new ();
7543 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7544 gtk_widget_show ( item );
7547 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7550 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7551 gtk_widget_show ( item );
7553 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7556 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7557 gtk_widget_show ( item );
7558 // Make it available only when a new track *not* already in progress
7559 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7561 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7562 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7563 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7564 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7565 gtk_widget_show ( item );
7567 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7568 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7569 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7570 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7571 gtk_widget_show ( item );
7573 GtkWidget *vis_submenu = gtk_menu_new ();
7574 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7575 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7576 gtk_widget_show ( item );
7577 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7579 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7580 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7581 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7582 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7583 gtk_widget_show ( item );
7585 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7586 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7588 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7589 gtk_widget_show ( item );
7591 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7592 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7593 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7594 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7596 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7597 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7598 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7599 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7601 gtk_widget_show ( item );
7603 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7604 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7605 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7606 gtk_widget_show ( item );
7610 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7611 GtkWidget *submenu_sort = gtk_menu_new ();
7612 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7613 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7614 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7615 gtk_widget_show ( item );
7616 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7618 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7619 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7621 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7622 gtk_widget_show ( item );
7624 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7625 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7626 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7627 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7628 gtk_widget_show ( item );
7631 GtkWidget *upload_submenu = gtk_menu_new ();
7633 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7635 item = gtk_menu_item_new ();
7636 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7637 gtk_widget_show ( item );
7639 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7640 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7641 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7642 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7643 if ( l->current_track ) {
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7645 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7646 gtk_widget_show ( item );
7649 item = gtk_menu_item_new ();
7650 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7651 gtk_widget_show ( item );
7654 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7655 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7657 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7658 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7659 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7660 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7661 gtk_widget_show ( item );
7663 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7664 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7665 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7666 gtk_widget_show ( item );
7668 GtkWidget *goto_submenu;
7669 goto_submenu = gtk_menu_new ();
7670 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7671 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7672 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7673 gtk_widget_show ( item );
7674 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7676 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7677 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7679 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7680 gtk_widget_show ( item );
7682 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7683 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7684 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7685 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7686 gtk_widget_show ( item );
7688 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7689 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7691 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7692 gtk_widget_show ( item );
7694 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7695 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7696 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7697 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7698 gtk_widget_show ( item );
7700 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7701 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7702 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7703 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7704 gtk_widget_show ( item );
7706 // Routes don't have speeds
7707 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7708 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7709 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7711 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7712 gtk_widget_show ( item );
7715 GtkWidget *combine_submenu;
7716 combine_submenu = gtk_menu_new ();
7717 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7718 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7719 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720 gtk_widget_show ( item );
7721 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7723 // Routes don't have times or segments...
7724 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7725 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7726 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7727 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7728 gtk_widget_show ( item );
7730 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7731 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7732 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7733 gtk_widget_show ( item );
7736 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7737 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7738 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7739 gtk_widget_show ( item );
7741 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7742 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7744 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7745 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7746 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7747 gtk_widget_show ( item );
7749 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7750 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7752 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7753 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7754 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7755 gtk_widget_show ( item );
7757 GtkWidget *split_submenu;
7758 split_submenu = gtk_menu_new ();
7759 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7760 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7761 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7762 gtk_widget_show ( item );
7763 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7765 // Routes don't have times or segments...
7766 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7767 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7768 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7769 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7770 gtk_widget_show ( item );
7772 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7773 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7774 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7775 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7776 gtk_widget_show ( item );
7779 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7780 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7781 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7782 gtk_widget_show ( item );
7784 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7785 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7786 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7787 gtk_widget_show ( item );
7788 // Make it available only when a trackpoint is selected.
7789 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7791 GtkWidget *insert_submenu = gtk_menu_new ();
7792 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7793 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7794 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7795 gtk_widget_show ( item );
7796 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7798 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7801 gtk_widget_show ( item );
7802 // Make it available only when a point is selected
7803 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7805 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7806 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7807 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7808 gtk_widget_show ( item );
7809 // Make it available only when a point is selected
7810 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7812 GtkWidget *delete_submenu;
7813 delete_submenu = gtk_menu_new ();
7814 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7815 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7816 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7817 gtk_widget_show ( item );
7818 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7820 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7823 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7824 gtk_widget_show ( item );
7825 // Make it available only when a point is selected
7826 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7828 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7829 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7830 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7831 gtk_widget_show ( item );
7833 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7834 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7835 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7836 gtk_widget_show ( item );
7838 GtkWidget *transform_submenu;
7839 transform_submenu = gtk_menu_new ();
7840 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7841 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7842 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7843 gtk_widget_show ( item );
7844 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7846 GtkWidget *dem_submenu;
7847 dem_submenu = gtk_menu_new ();
7848 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7849 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
7850 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7851 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7853 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7854 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7855 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7856 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7857 gtk_widget_show ( item );
7859 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7860 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7861 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7862 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7863 gtk_widget_show ( item );
7865 GtkWidget *smooth_submenu;
7866 smooth_submenu = gtk_menu_new ();
7867 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7868 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7869 gtk_widget_show ( item );
7870 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7872 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7873 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7874 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7875 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7876 gtk_widget_show ( item );
7878 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7881 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7882 gtk_widget_show ( item );
7884 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7885 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7887 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7888 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7889 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7890 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7891 gtk_widget_show ( item );
7893 // Routes don't have timestamps - so this is only available for tracks
7894 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7895 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7897 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7898 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7899 gtk_widget_show ( item );
7902 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7903 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7905 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7906 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7907 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7908 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7909 gtk_widget_show ( item );
7911 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7912 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7913 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7915 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7916 gtk_widget_show ( item );
7919 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7921 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7922 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7924 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7925 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
7926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7927 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7928 gtk_widget_show ( item );
7931 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7934 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7935 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7937 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7938 gtk_widget_show ( item );
7940 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7941 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7943 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7944 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7946 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7947 gtk_widget_show ( item );
7949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7950 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7951 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
7952 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7953 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7954 gtk_widget_show ( item );
7957 // ATM can't upload a single waypoint but can do waypoints to a GPS
7958 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7959 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7960 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7961 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7962 gtk_widget_show ( item );
7963 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7965 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7966 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7967 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7968 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7969 gtk_widget_show ( item );
7973 #ifdef VIK_CONFIG_GOOGLE
7974 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7979 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7980 gtk_widget_show ( item );
7984 // Some things aren't usable with routes
7985 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7986 #ifdef VIK_CONFIG_OPENSTREETMAP
7987 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7988 // Convert internal pointer into actual track for usage outside this file
7989 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7990 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7992 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7993 gtk_widget_show ( item );
7996 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7997 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7999 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8000 gtk_widget_show ( item );
8002 /* ATM This function is only available via the layers panel, due to needing a vlp */
8004 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8005 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8006 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8008 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8009 gtk_widget_show ( item );
8013 #ifdef VIK_CONFIG_GEOTAG
8014 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8016 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8017 gtk_widget_show ( item );
8021 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8022 // Only show on viewport popmenu when a trackpoint is selected
8023 if ( ! vlp && l->current_tpl ) {
8025 item = gtk_menu_item_new ();
8026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8027 gtk_widget_show ( item );
8029 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8030 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8032 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8033 gtk_widget_show ( item );
8037 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8038 GtkWidget *transform_submenu;
8039 transform_submenu = gtk_menu_new ();
8040 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8042 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8043 gtk_widget_show ( item );
8044 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8046 GtkWidget *dem_submenu;
8047 dem_submenu = gtk_menu_new ();
8048 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8049 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
8050 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8051 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8053 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8056 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8057 gtk_widget_show ( item );
8059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8061 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8062 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8063 gtk_widget_show ( item );
8066 gtk_widget_show_all ( GTK_WIDGET(menu) );
8071 // TODO: Probably better to rework this track manipulation in viktrack.c
8072 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8075 if (!vtl->current_tpl)
8078 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8079 VikTrackpoint *tp_other = NULL;
8082 if (!vtl->current_tpl->prev)
8084 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8086 if (!vtl->current_tpl->next)
8088 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8091 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8094 VikTrackpoint *tp_new = vik_trackpoint_new();
8095 struct LatLon ll_current, ll_other;
8096 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8097 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8099 /* main positional interpolation */
8100 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8101 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8103 /* Now other properties that can be interpolated */
8104 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8106 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8107 /* Note here the division is applied to each part, then added
8108 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8109 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8110 tp_new->has_timestamp = TRUE;
8113 if (tp_current->speed != NAN && tp_other->speed != NAN)
8114 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8116 /* TODO - improve interpolation of course, as it may not be correct.
8117 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8118 [similar applies if value is in radians] */
8119 if (tp_current->course != NAN && tp_other->course != NAN)
8120 tp_new->course = (tp_current->course + tp_other->course)/2;
8122 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8124 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8125 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8127 // Otherwise try routes
8128 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8132 gint index = g_list_index ( trk->trackpoints, tp_current );
8136 // NB no recalculation of bounds since it is inserted between points
8137 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8142 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8148 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8152 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8154 if ( vtl->current_tpl )
8156 vtl->current_tpl = NULL;
8157 vtl->current_tp_track = NULL;
8158 vtl->current_tp_id = NULL;
8159 vik_layer_emit_update(VIK_LAYER(vtl));
8163 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8165 g_assert ( vtl->tpwin != NULL );
8166 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8167 trw_layer_cancel_current_tp ( vtl, TRUE );
8169 if ( vtl->current_tpl == NULL )
8172 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8174 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8175 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8177 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8179 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8181 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8185 trw_layer_trackpoint_selected_delete ( vtl, tr );
8187 if ( vtl->current_tpl )
8188 // Reset dialog with the available adjacent trackpoint
8189 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8191 vik_layer_emit_update(VIK_LAYER(vtl));
8193 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8195 if ( vtl->current_tp_track )
8196 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8197 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8199 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8201 if ( vtl->current_tp_track )
8202 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8203 vik_layer_emit_update(VIK_LAYER(vtl));
8205 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8207 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8208 vik_layer_emit_update(VIK_LAYER(vtl));
8210 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8211 vik_layer_emit_update(VIK_LAYER(vtl));
8215 * trw_layer_dialog_shift:
8216 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8218 * Try to reposition a dialog if it's over the specified coord
8219 * so to not obscure the item of interest
8221 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8223 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8225 // Attempt force dialog to be shown so we can find out where it is more reliably...
8226 while ( gtk_events_pending() )
8227 gtk_main_iteration ();
8229 // get parent window position & size
8230 gint win_pos_x, win_pos_y;
8231 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8233 gint win_size_x, win_size_y;
8234 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8236 // get own dialog size
8237 gint dia_size_x, dia_size_y;
8238 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8240 // get own dialog position
8241 gint dia_pos_x, dia_pos_y;
8242 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8244 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8245 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8247 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8249 gint vp_xx, vp_yy; // In viewport pixels
8250 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8252 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8256 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8258 // Transform Viewport pixels into absolute pixels
8259 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8260 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8262 // Is dialog over the point (to within an ^^ edge value)
8263 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8264 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8268 gint hh = vik_viewport_get_height ( vvp );
8270 // Consider the difference in viewport to the full window
8271 gint offset_y = dest_y;
8272 // Add difference between dialog and window sizes
8273 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8275 if ( vp_yy > hh/2 ) {
8276 // Point in bottom half, move window to top half
8277 gtk_window_move ( dialog, dia_pos_x, offset_y );
8280 // Point in top half, move dialog down
8281 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8285 // Shift left<->right
8286 gint ww = vik_viewport_get_width ( vvp );
8288 // Consider the difference in viewport to the full window
8289 gint offset_x = dest_x;
8290 // Add difference between dialog and window sizes
8291 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8293 if ( vp_xx > ww/2 ) {
8294 // Point on right, move window to left
8295 gtk_window_move ( dialog, offset_x, dia_pos_y );
8298 // Point on left, move right
8299 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8307 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8311 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8312 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8313 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8314 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8316 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8318 if ( vtl->current_tpl ) {
8319 // get tp pixel position
8320 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8322 // Shift up<->down to try not to obscure the trackpoint.
8323 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8327 if ( vtl->current_tpl )
8328 if ( vtl->current_tp_track )
8329 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8330 /* set layer name and TP data */
8333 /***************************************************************************
8335 ***************************************************************************/
8337 /*** Utility data structures and functions ****/
8341 gint closest_x, closest_y;
8342 gboolean draw_images;
8343 gpointer *closest_wp_id;
8344 VikWaypoint *closest_wp;
8350 gint closest_x, closest_y;
8351 gpointer closest_track_id;
8352 VikTrackpoint *closest_tp;
8358 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8364 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8366 // If waypoint has an image then use the image size to select
8367 if ( params->draw_images && wp->image ) {
8368 gint slackx, slacky;
8369 slackx = wp->image_width / 2;
8370 slacky = wp->image_height / 2;
8372 if ( x <= params->x + slackx && x >= params->x - slackx
8373 && y <= params->y + slacky && y >= params->y - slacky ) {
8374 params->closest_wp_id = id;
8375 params->closest_wp = wp;
8376 params->closest_x = x;
8377 params->closest_y = y;
8380 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8381 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8382 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8384 params->closest_wp_id = id;
8385 params->closest_wp = wp;
8386 params->closest_x = x;
8387 params->closest_y = y;
8391 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8393 GList *tpl = t->trackpoints;
8399 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8405 tp = VIK_TRACKPOINT(tpl->data);
8407 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8409 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8410 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8411 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8413 params->closest_track_id = id;
8414 params->closest_tp = tp;
8415 params->closest_tpl = tpl;
8416 params->closest_x = x;
8417 params->closest_y = y;
8423 // ATM: Leave this as 'Track' only.
8424 // Not overly bothered about having a snap to route trackpoint capability
8425 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8427 TPSearchParams params;
8431 params.closest_track_id = NULL;
8432 params.closest_tp = NULL;
8433 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8434 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8435 return params.closest_tp;
8438 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8440 WPSearchParams params;
8444 params.draw_images = vtl->drawimages;
8445 params.closest_wp = NULL;
8446 params.closest_wp_id = NULL;
8447 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8448 return params.closest_wp;
8452 // Some forward declarations
8453 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8454 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8455 static void marker_end_move ( tool_ed_t *t );
8458 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8462 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8464 // Here always allow snapping back to the original location
8465 // this is useful when one decides not to move the thing afterall
8466 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8469 if ( event->state & GDK_CONTROL_MASK )
8471 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8473 new_coord = tp->coord;
8477 if ( event->state & GDK_SHIFT_MASK )
8479 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8481 new_coord = wp->coord;
8485 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8487 marker_moveto ( t, x, y );
8494 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8496 if ( t->holding && event->button == 1 )
8499 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8502 if ( event->state & GDK_CONTROL_MASK )
8504 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8506 new_coord = tp->coord;
8510 if ( event->state & GDK_SHIFT_MASK )
8512 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8514 new_coord = wp->coord;
8517 marker_end_move ( t );
8519 // Determine if working on a waypoint or a trackpoint
8520 if ( t->is_waypoint ) {
8521 // Update waypoint position
8522 vtl->current_wp->coord = new_coord;
8523 trw_layer_calculate_bounds_waypoints ( vtl );
8524 // Reset waypoint pointer
8525 vtl->current_wp = NULL;
8526 vtl->current_wp_id = NULL;
8529 if ( vtl->current_tpl ) {
8530 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8532 if ( vtl->current_tp_track )
8533 vik_track_calculate_bounds ( vtl->current_tp_track );
8536 if ( vtl->current_tp_track )
8537 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8538 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8542 vik_layer_emit_update ( VIK_LAYER(vtl) );
8549 Returns true if a waypoint or track is found near the requested event position for this particular layer
8550 The item found is automatically selected
8551 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8553 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8555 if ( event->button != 1 )
8558 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8561 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8565 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8567 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8569 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8570 WPSearchParams wp_params;
8571 wp_params.vvp = vvp;
8572 wp_params.x = event->x;
8573 wp_params.y = event->y;
8574 wp_params.draw_images = vtl->drawimages;
8575 wp_params.closest_wp_id = NULL;
8576 wp_params.closest_wp = NULL;
8578 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8580 if ( wp_params.closest_wp ) {
8583 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8585 // Too easy to move it so must be holding shift to start immediately moving it
8586 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8587 if ( event->state & GDK_SHIFT_MASK ||
8588 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8589 // Put into 'move buffer'
8590 // NB vvp & vw already set in tet
8591 tet->vtl = (gpointer)vtl;
8592 tet->is_waypoint = TRUE;
8594 marker_begin_move (tet, event->x, event->y);
8597 vtl->current_wp = wp_params.closest_wp;
8598 vtl->current_wp_id = wp_params.closest_wp_id;
8600 vik_layer_emit_update ( VIK_LAYER(vtl) );
8606 // Used for both track and route lists
8607 TPSearchParams tp_params;
8608 tp_params.vvp = vvp;
8609 tp_params.x = event->x;
8610 tp_params.y = event->y;
8611 tp_params.closest_track_id = NULL;
8612 tp_params.closest_tp = NULL;
8613 tp_params.closest_tpl = NULL;
8614 tp_params.bbox = bbox;
8616 if (vtl->tracks_visible) {
8617 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8619 if ( tp_params.closest_tp ) {
8621 // Always select + highlight the track
8622 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8624 tet->is_waypoint = FALSE;
8626 // Select the Trackpoint
8627 // Can move it immediately when control held or it's the previously selected tp
8628 if ( event->state & GDK_CONTROL_MASK ||
8629 vtl->current_tpl == tp_params.closest_tpl ) {
8630 // Put into 'move buffer'
8631 // NB vvp & vw already set in tet
8632 tet->vtl = (gpointer)vtl;
8633 marker_begin_move (tet, event->x, event->y);
8636 vtl->current_tpl = tp_params.closest_tpl;
8637 vtl->current_tp_id = tp_params.closest_track_id;
8638 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8640 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8643 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8645 vik_layer_emit_update ( VIK_LAYER(vtl) );
8650 // Try again for routes
8651 if (vtl->routes_visible) {
8652 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8654 if ( tp_params.closest_tp ) {
8656 // Always select + highlight the track
8657 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8659 tet->is_waypoint = FALSE;
8661 // Select the Trackpoint
8662 // Can move it immediately when control held or it's the previously selected tp
8663 if ( event->state & GDK_CONTROL_MASK ||
8664 vtl->current_tpl == tp_params.closest_tpl ) {
8665 // Put into 'move buffer'
8666 // NB vvp & vw already set in tet
8667 tet->vtl = (gpointer)vtl;
8668 marker_begin_move (tet, event->x, event->y);
8671 vtl->current_tpl = tp_params.closest_tpl;
8672 vtl->current_tp_id = tp_params.closest_track_id;
8673 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8675 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8678 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8680 vik_layer_emit_update ( VIK_LAYER(vtl) );
8685 /* these aren't the droids you're looking for */
8686 vtl->current_wp = NULL;
8687 vtl->current_wp_id = NULL;
8688 trw_layer_cancel_current_tp ( vtl, FALSE );
8691 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8696 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8698 if ( event->button != 3 )
8701 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8704 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8707 /* Post menu for the currently selected item */
8709 /* See if a track is selected */
8710 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8711 if ( track && track->visible ) {
8713 if ( track->name ) {
8715 if ( vtl->track_right_click_menu )
8716 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8718 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8725 if ( track->is_route )
8726 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8728 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8730 if ( trkf && udataU.uuid ) {
8733 if ( track->is_route )
8734 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8736 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8738 trw_layer_sublayer_add_menu_items ( vtl,
8739 vtl->track_right_click_menu,
8741 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8747 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8753 /* See if a waypoint is selected */
8754 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8755 if ( waypoint && waypoint->visible ) {
8756 if ( waypoint->name ) {
8758 if ( vtl->wp_right_click_menu )
8759 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8761 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8764 udata.wp = waypoint;
8767 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8769 if ( wpf && udata.uuid ) {
8770 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8772 trw_layer_sublayer_add_menu_items ( vtl,
8773 vtl->wp_right_click_menu,
8775 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8780 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8789 /* background drawing hook, to be passed the viewport */
8790 static gboolean tool_sync_done = TRUE;
8792 static gboolean tool_sync(gpointer data)
8794 VikViewport *vvp = data;
8795 gdk_threads_enter();
8796 vik_viewport_sync(vvp);
8797 tool_sync_done = TRUE;
8798 gdk_threads_leave();
8802 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8805 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8806 gdk_gc_set_function ( t->gc, GDK_INVERT );
8807 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8808 vik_viewport_sync(t->vvp);
8813 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8815 VikViewport *vvp = t->vvp;
8816 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8817 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8821 if (tool_sync_done) {
8822 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8823 tool_sync_done = FALSE;
8827 static void marker_end_move ( tool_ed_t *t )
8829 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8830 g_object_unref ( t->gc );
8834 /*** Edit waypoint ****/
8836 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8838 tool_ed_t *t = g_new(tool_ed_t, 1);
8844 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8849 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8851 WPSearchParams params;
8852 tool_ed_t *t = data;
8853 VikViewport *vvp = t->vvp;
8855 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8862 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8865 if ( vtl->current_wp && vtl->current_wp->visible )
8867 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8869 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8871 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8872 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8874 if ( event->button == 3 )
8875 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8877 marker_begin_move(t, event->x, event->y);
8884 params.x = event->x;
8885 params.y = event->y;
8886 params.draw_images = vtl->drawimages;
8887 params.closest_wp_id = NULL;
8888 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8889 params.closest_wp = NULL;
8890 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8891 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8893 // how do we get here?
8894 marker_begin_move(t, event->x, event->y);
8895 g_critical("shouldn't be here");
8898 else if ( params.closest_wp )
8900 if ( event->button == 3 )
8901 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8903 vtl->waypoint_rightclick = FALSE;
8905 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8907 vtl->current_wp = params.closest_wp;
8908 vtl->current_wp_id = params.closest_wp_id;
8910 /* could make it so don't update if old WP is off screen and new is null but oh well */
8911 vik_layer_emit_update ( VIK_LAYER(vtl) );
8915 vtl->current_wp = NULL;
8916 vtl->current_wp_id = NULL;
8917 vtl->waypoint_rightclick = FALSE;
8918 vik_layer_emit_update ( VIK_LAYER(vtl) );
8922 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8924 tool_ed_t *t = data;
8925 VikViewport *vvp = t->vvp;
8927 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8932 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8935 if ( event->state & GDK_CONTROL_MASK )
8937 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8939 new_coord = tp->coord;
8943 if ( event->state & GDK_SHIFT_MASK )
8945 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8946 if ( wp && wp != vtl->current_wp )
8947 new_coord = wp->coord;
8952 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8954 marker_moveto ( t, x, y );
8961 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8963 tool_ed_t *t = data;
8964 VikViewport *vvp = t->vvp;
8966 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8969 if ( t->holding && event->button == 1 )
8972 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8975 if ( event->state & GDK_CONTROL_MASK )
8977 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8979 new_coord = tp->coord;
8983 if ( event->state & GDK_SHIFT_MASK )
8985 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8986 if ( wp && wp != vtl->current_wp )
8987 new_coord = wp->coord;
8990 marker_end_move ( t );
8992 vtl->current_wp->coord = new_coord;
8994 trw_layer_calculate_bounds_waypoints ( vtl );
8995 vik_layer_emit_update ( VIK_LAYER(vtl) );
8998 /* PUT IN RIGHT PLACE!!! */
8999 if ( event->button == 3 && vtl->waypoint_rightclick )
9001 if ( vtl->wp_right_click_menu )
9002 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9003 if ( vtl->current_wp ) {
9004 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9005 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 );
9006 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9008 vtl->waypoint_rightclick = FALSE;
9013 /*** New track ****/
9015 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9022 GdkDrawable *drawable;
9028 * Draw specified pixmap
9030 static gboolean draw_sync ( gpointer data )
9032 draw_sync_t *ds = (draw_sync_t*) data;
9033 // Sometimes don't want to draw
9034 // normally because another update has taken precedent such as panning the display
9035 // which means this pixmap is no longer valid
9036 if ( ds->vtl->draw_sync_do ) {
9037 gdk_threads_enter();
9038 gdk_draw_drawable (ds->drawable,
9041 0, 0, 0, 0, -1, -1);
9042 ds->vtl->draw_sync_done = TRUE;
9043 gdk_threads_leave();
9049 static gchar* distance_string (gdouble distance)
9053 /* draw label with distance */
9054 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9055 switch (dist_units) {
9056 case VIK_UNITS_DISTANCE_MILES:
9057 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9058 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9059 } else if (distance < 1609.4) {
9060 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9062 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9066 // VIK_UNITS_DISTANCE_KILOMETRES
9067 if (distance >= 1000 && distance < 100000) {
9068 g_sprintf(str, "%3.2f km", distance/1000.0);
9069 } else if (distance < 1000) {
9070 g_sprintf(str, "%d m", (int)distance);
9072 g_sprintf(str, "%d km", (int)distance/1000);
9076 return g_strdup (str);
9080 * Actually set the message in statusbar
9082 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9084 // Only show elevation data when track has some elevation properties
9085 gchar str_gain_loss[64];
9086 str_gain_loss[0] = '\0';
9087 gchar str_last_step[64];
9088 str_last_step[0] = '\0';
9089 gchar *str_total = distance_string (distance);
9091 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9092 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9093 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9095 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9098 if ( last_step > 0 ) {
9099 gchar *tmp = distance_string (last_step);
9100 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9104 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9106 // Write with full gain/loss information
9107 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9108 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9110 g_free ( str_total );
9114 * Figure out what information should be set in the statusbar and then write it
9116 static void update_statusbar ( VikTrwLayer *vtl )
9118 // Get elevation data
9119 gdouble elev_gain, elev_loss;
9120 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9122 /* Find out actual distance of current track */
9123 gdouble distance = vik_track_get_length (vtl->current_track);
9125 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9129 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9131 /* if we haven't sync'ed yet, we don't have time to do more. */
9132 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9133 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9134 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9136 static GdkPixmap *pixmap = NULL;
9138 // Need to check in case window has been resized
9139 w1 = vik_viewport_get_width(vvp);
9140 h1 = vik_viewport_get_height(vvp);
9142 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9144 gdk_drawable_get_size (pixmap, &w2, &h2);
9145 if (w1 != w2 || h1 != h2) {
9146 g_object_unref ( G_OBJECT ( pixmap ) );
9147 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9150 // Reset to background
9151 gdk_draw_drawable (pixmap,
9152 vtl->current_track_newpoint_gc,
9153 vik_viewport_get_pixmap(vvp),
9154 0, 0, 0, 0, -1, -1);
9156 draw_sync_t *passalong;
9159 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9161 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9162 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9163 // thus when we come to reset to the background it would include what we have already drawn!!
9164 gdk_draw_line ( pixmap,
9165 vtl->current_track_newpoint_gc,
9166 x1, y1, event->x, event->y );
9167 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9169 /* Find out actual distance of current track */
9170 gdouble distance = vik_track_get_length (vtl->current_track);
9172 // Now add distance to where the pointer is //
9175 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9176 vik_coord_to_latlon ( &coord, &ll );
9177 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9178 distance = distance + last_step;
9180 // Get elevation data
9181 gdouble elev_gain, elev_loss;
9182 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9184 // Adjust elevation data (if available) for the current pointer position
9186 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9187 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9188 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9189 // Adjust elevation of last track point
9190 if ( elev_new > last_tpt->altitude )
9192 elev_gain += elev_new - last_tpt->altitude;
9195 elev_loss += last_tpt->altitude - elev_new;
9200 // Display of the distance 'tooltip' during track creation is controlled by a preference
9202 if ( a_vik_get_create_track_tooltip() ) {
9204 gchar *str = distance_string (distance);
9206 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9207 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9208 pango_layout_set_text (pl, str, -1);
9210 pango_layout_get_pixel_size ( pl, &wd, &hd );
9213 // offset from cursor a bit depending on font size
9217 // Create a background block to make the text easier to read over the background map
9218 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9219 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9220 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9222 g_object_unref ( G_OBJECT ( pl ) );
9223 g_object_unref ( G_OBJECT ( background_block_gc ) );
9227 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9228 passalong->vtl = vtl;
9229 passalong->pixmap = pixmap;
9230 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9231 passalong->gc = vtl->current_track_newpoint_gc;
9235 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9237 // Update statusbar with full gain/loss information
9238 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9240 // draw pixmap when we have time to
9241 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9242 vtl->draw_sync_done = FALSE;
9243 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9245 return VIK_LAYER_TOOL_ACK;
9248 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9250 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9251 vtl->current_track = NULL;
9252 vik_layer_emit_update ( VIK_LAYER(vtl) );
9254 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9256 if ( vtl->current_track->trackpoints )
9258 GList *last = g_list_last(vtl->current_track->trackpoints);
9259 g_free ( last->data );
9260 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9263 update_statusbar ( vtl );
9265 vik_layer_emit_update ( VIK_LAYER(vtl) );
9272 * Common function to handle trackpoint button requests on either a route or a track
9273 * . enables adding a point via normal click
9274 * . enables removal of last point via right click
9275 * . finishing of the track or route via double clicking
9277 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9281 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9284 if ( event->button == 2 ) {
9285 // As the display is panning, the new track pixmap is now invalid so don't draw it
9286 // otherwise this drawing done results in flickering back to an old image
9287 vtl->draw_sync_do = FALSE;
9291 if ( event->button == 3 )
9293 if ( !vtl->current_track )
9296 if ( vtl->current_track->trackpoints )
9298 GList *last = g_list_last(vtl->current_track->trackpoints);
9299 g_free ( last->data );
9300 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9302 vik_track_calculate_bounds ( vtl->current_track );
9303 update_statusbar ( vtl );
9305 vik_layer_emit_update ( VIK_LAYER(vtl) );
9309 if ( event->type == GDK_2BUTTON_PRESS )
9311 /* subtract last (duplicate from double click) tp then end */
9312 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9314 GList *last = g_list_last(vtl->current_track->trackpoints);
9315 g_free ( last->data );
9316 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9317 /* undo last, then end */
9318 vtl->current_track = NULL;
9320 vik_layer_emit_update ( VIK_LAYER(vtl) );
9324 tp = vik_trackpoint_new();
9325 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9327 /* snap to other TP */
9328 if ( event->state & GDK_CONTROL_MASK )
9330 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9332 tp->coord = other_tp->coord;
9335 tp->newsegment = FALSE;
9336 tp->has_timestamp = FALSE;
9339 if ( vtl->current_track ) {
9340 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9341 /* Auto attempt to get elevation from DEM data (if it's available) */
9342 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9345 vtl->ct_x1 = vtl->ct_x2;
9346 vtl->ct_y1 = vtl->ct_y2;
9347 vtl->ct_x2 = event->x;
9348 vtl->ct_y2 = event->y;
9350 vik_layer_emit_update ( VIK_LAYER(vtl) );
9354 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9356 // ----------------------------------------------------- if current is a route - switch to new track
9357 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9359 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9360 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9362 new_track_create_common ( vtl, name );
9368 return tool_new_track_or_route_click ( vtl, event, vvp );
9371 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9373 if ( event->button == 2 ) {
9374 // Pan moving ended - enable potential point drawing again
9375 vtl->draw_sync_do = TRUE;
9376 vtl->draw_sync_done = TRUE;
9380 /*** New route ****/
9382 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9387 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9389 // -------------------------- if current is a track - switch to new route
9390 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9392 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9393 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9394 new_route_create_common ( vtl, name );
9400 return tool_new_track_or_route_click ( vtl, event, vvp );
9403 /*** New waypoint ****/
9405 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9410 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9413 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9415 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9416 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9417 trw_layer_calculate_bounds_waypoints ( vtl );
9418 vik_layer_emit_update ( VIK_LAYER(vtl) );
9424 /*** Edit trackpoint ****/
9426 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9428 tool_ed_t *t = g_new(tool_ed_t, 1);
9434 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9439 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9441 tool_ed_t *t = data;
9442 VikViewport *vvp = t->vvp;
9443 TPSearchParams params;
9444 /* OUTDATED DOCUMENTATION:
9445 find 5 pixel range on each side. then put these UTM, and a pointer
9446 to the winning track name (and maybe the winning track itself), and a
9447 pointer to the winning trackpoint, inside an array or struct. pass
9448 this along, do a foreach on the tracks which will do a foreach on the
9451 params.x = event->x;
9452 params.y = event->y;
9453 params.closest_track_id = NULL;
9454 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9455 params.closest_tp = NULL;
9456 params.closest_tpl = NULL;
9457 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9459 if ( event->button != 1 )
9462 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9465 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9468 if ( vtl->current_tpl )
9470 /* 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.) */
9471 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9472 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9477 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9479 if ( current_tr->visible &&
9480 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9481 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9482 marker_begin_move ( t, event->x, event->y );
9488 if ( vtl->tracks_visible )
9489 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9491 if ( params.closest_tp )
9493 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9494 vtl->current_tpl = params.closest_tpl;
9495 vtl->current_tp_id = params.closest_track_id;
9496 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9497 trw_layer_tpwin_init ( vtl );
9498 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9499 vik_layer_emit_update ( VIK_LAYER(vtl) );
9503 if ( vtl->routes_visible )
9504 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9506 if ( params.closest_tp )
9508 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9509 vtl->current_tpl = params.closest_tpl;
9510 vtl->current_tp_id = params.closest_track_id;
9511 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9512 trw_layer_tpwin_init ( vtl );
9513 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9514 vik_layer_emit_update ( VIK_LAYER(vtl) );
9518 /* these aren't the droids you're looking for */
9522 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9524 tool_ed_t *t = data;
9525 VikViewport *vvp = t->vvp;
9527 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9533 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9536 if ( event->state & GDK_CONTROL_MASK )
9538 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9539 if ( tp && tp != vtl->current_tpl->data )
9540 new_coord = tp->coord;
9542 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9545 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9546 marker_moveto ( t, x, y );
9554 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9556 tool_ed_t *t = data;
9557 VikViewport *vvp = t->vvp;
9559 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9561 if ( event->button != 1)
9566 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9569 if ( event->state & GDK_CONTROL_MASK )
9571 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9572 if ( tp && tp != vtl->current_tpl->data )
9573 new_coord = tp->coord;
9576 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9577 if ( vtl->current_tp_track )
9578 vik_track_calculate_bounds ( vtl->current_tp_track );
9580 marker_end_move ( t );
9582 /* diff dist is diff from orig */
9584 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9586 vik_layer_emit_update ( VIK_LAYER(vtl) );
9593 /*** Route Finder ***/
9594 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9599 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9602 if ( !vtl ) return FALSE;
9603 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9604 if ( event->button == 3 && vtl->route_finder_current_track ) {
9606 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9608 vtl->route_finder_coord = *new_end;
9610 vik_layer_emit_update ( VIK_LAYER(vtl) );
9611 /* remove last ' to:...' */
9612 if ( vtl->route_finder_current_track->comment ) {
9613 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9614 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9615 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9616 last_to - vtl->route_finder_current_track->comment - 1);
9617 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9622 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9623 struct LatLon start, end;
9625 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9626 vik_coord_to_latlon ( &(tmp), &end );
9627 vtl->route_finder_coord = tmp; /* for continuations */
9629 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9630 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9631 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9633 vtl->route_finder_check_added_track = TRUE;
9634 vtl->route_finder_started = FALSE;
9637 vik_routing_default_find ( vtl, start, end);
9639 /* see if anything was done -- a track was added or appended to */
9640 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9641 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 ) );
9642 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9643 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9644 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9645 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9648 if ( vtl->route_finder_added_track )
9649 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9651 vtl->route_finder_added_track = NULL;
9652 vtl->route_finder_check_added_track = FALSE;
9653 vtl->route_finder_append = FALSE;
9655 vik_layer_emit_update ( VIK_LAYER(vtl) );
9657 vtl->route_finder_started = TRUE;
9658 vtl->route_finder_coord = tmp;
9659 vtl->route_finder_current_track = NULL;
9664 /*** Show picture ****/
9666 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9671 /* Params are: vvp, event, last match found or NULL */
9672 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9674 if ( wp->image && wp->visible )
9676 gint x, y, slackx, slacky;
9677 GdkEventButton *event = (GdkEventButton *) params[1];
9679 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9680 slackx = wp->image_width / 2;
9681 slacky = wp->image_height / 2;
9682 if ( x <= event->x + slackx && x >= event->x - slackx
9683 && y <= event->y + slacky && y >= event->y - slacky )
9685 params[2] = wp->image; /* we've found a match. however continue searching
9686 * since we want to find the last match -- that
9687 * is, the match that was drawn last. */
9692 static void trw_layer_show_picture ( gpointer pass_along[6] )
9694 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9696 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9699 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9700 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9701 g_free ( quoted_file );
9702 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9704 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() );
9705 g_error_free ( err );
9708 #endif /* WINDOWS */
9711 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9713 gpointer params[3] = { vvp, event, NULL };
9714 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9716 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9719 static gpointer pass_along[6];
9720 pass_along[0] = vtl;
9721 pass_along[5] = params[2];
9722 trw_layer_show_picture ( pass_along );
9723 return TRUE; /* found a match */
9726 return FALSE; /* go through other layers, searching for a match */
9729 /***************************************************************************
9731 ***************************************************************************/
9734 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9736 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9737 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9740 /* Structure for thumbnail creating data used in the background thread */
9742 VikTrwLayer *vtl; // Layer needed for redrawing
9743 GSList *pics; // Image list
9744 } thumbnail_create_thread_data;
9746 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9748 guint total = g_slist_length(tctd->pics), done = 0;
9749 while ( tctd->pics )
9751 a_thumbnails_create ( (gchar *) tctd->pics->data );
9752 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9754 return -1; /* Abort thread */
9756 tctd->pics = tctd->pics->next;
9759 // Redraw to show the thumbnails as they are now created
9760 if ( IS_VIK_LAYER(tctd->vtl) )
9761 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9766 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9768 while ( tctd->pics )
9770 g_free ( tctd->pics->data );
9771 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9776 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9778 if ( ! vtl->has_verified_thumbnails )
9780 GSList *pics = NULL;
9781 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9784 gint len = g_slist_length ( pics );
9785 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9786 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9789 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9791 (vik_thr_func) create_thumbnails_thread,
9793 (vik_thr_free_func) thumbnail_create_thread_free,
9801 static const gchar* my_track_colors ( gint ii )
9803 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9815 // Fast and reliable way of returning a colour
9816 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9819 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9821 GHashTableIter iter;
9822 gpointer key, value;
9826 g_hash_table_iter_init ( &iter, vtl->tracks );
9828 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9830 // Tracks get a random spread of colours if not already assigned
9831 if ( ! VIK_TRACK(value)->has_color ) {
9832 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9833 VIK_TRACK(value)->color = vtl->track_color;
9835 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9837 VIK_TRACK(value)->has_color = TRUE;
9840 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9843 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9849 g_hash_table_iter_init ( &iter, vtl->routes );
9851 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9853 // Routes get an intermix of reds
9854 if ( ! VIK_TRACK(value)->has_color ) {
9856 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9858 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9859 VIK_TRACK(value)->has_color = TRUE;
9862 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9869 * (Re)Calculate the bounds of the waypoints in this layer,
9870 * This should be called whenever waypoints are changed
9872 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9874 struct LatLon topleft = { 0.0, 0.0 };
9875 struct LatLon bottomright = { 0.0, 0.0 };
9878 GHashTableIter iter;
9879 gpointer key, value;
9881 g_hash_table_iter_init ( &iter, vtl->waypoints );
9883 // Set bounds to first point
9884 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9885 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9886 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9889 // Ensure there is another point...
9890 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9892 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9894 // See if this point increases the bounds.
9895 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9897 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9898 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9899 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9900 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9904 vtl->waypoints_bbox.north = topleft.lat;
9905 vtl->waypoints_bbox.east = bottomright.lon;
9906 vtl->waypoints_bbox.south = bottomright.lat;
9907 vtl->waypoints_bbox.west = topleft.lon;
9910 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9912 vik_track_calculate_bounds ( trk );
9915 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9917 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9918 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9921 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9923 if ( ! VIK_LAYER(vtl)->vt )
9926 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9927 if ( g_hash_table_size (vtl->tracks) > 1 )
9928 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9930 if ( g_hash_table_size (vtl->routes) > 1 )
9931 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9933 if ( g_hash_table_size (vtl->waypoints) > 1 )
9934 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9937 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9939 if ( VIK_LAYER(vtl)->realized )
9940 trw_layer_verify_thumbnails ( vtl, vvp );
9941 trw_layer_track_alloc_colors ( vtl );
9943 trw_layer_calculate_bounds_waypoints ( vtl );
9944 trw_layer_calculate_bounds_tracks ( vtl );
9946 // Apply treeview sort after loading all the tracks for this layer
9947 // (rather than sorted insert on each individual track additional)
9948 // and after subsequent changes to the properties as the specified order may have changed.
9949 // since the sorting of a treeview section is now very quick
9950 // NB sorting is also performed after every name change as well to maintain the list order
9951 trw_layer_sort_all ( vtl );
9954 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9956 return vtl->coord_mode;
9960 * Uniquify the whole layer
9961 * Also requires the layers panel as the names shown there need updating too
9962 * Returns whether the operation was successful or not
9964 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9967 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9968 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9969 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9975 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9977 vik_coord_convert ( &(wp->coord), *dest_mode );
9980 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9982 vik_track_convert ( tr, *dest_mode );
9985 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9987 if ( vtl->coord_mode != dest_mode )
9989 vtl->coord_mode = dest_mode;
9990 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9991 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9992 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9996 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9998 vtl->menu_selection = selection;
10001 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10003 return (vtl->menu_selection);
10006 /* ----------- Downloading maps along tracks --------------- */
10008 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10010 /* TODO: calculating based on current size of viewport */
10011 const gdouble w_at_zoom_0_125 = 0.0013;
10012 const gdouble h_at_zoom_0_125 = 0.0011;
10013 gdouble zoom_factor = zoom_level/0.125;
10015 wh->lat = h_at_zoom_0_125 * zoom_factor;
10016 wh->lon = w_at_zoom_0_125 * zoom_factor;
10018 return 0; /* all OK */
10021 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10023 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10024 (dist->lat >= ABS(to->north_south - from->north_south)))
10027 VikCoord *coord = g_malloc(sizeof(VikCoord));
10028 coord->mode = VIK_COORD_LATLON;
10030 if (ABS(gradient) < 1) {
10031 if (from->east_west > to->east_west)
10032 coord->east_west = from->east_west - dist->lon;
10034 coord->east_west = from->east_west + dist->lon;
10035 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10037 if (from->north_south > to->north_south)
10038 coord->north_south = from->north_south - dist->lat;
10040 coord->north_south = from->north_south + dist->lat;
10041 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10047 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10049 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10050 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10052 VikCoord *next = from;
10054 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10056 list = g_list_prepend(list, next);
10062 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10064 typedef struct _Rect {
10069 #define GLRECT(iter) ((Rect *)((iter)->data))
10072 GList *rects_to_download = NULL;
10075 if (get_download_area_width(vvp, zoom_level, &wh))
10078 GList *iter = tr->trackpoints;
10082 gboolean new_map = TRUE;
10083 VikCoord *cur_coord, tl, br;
10086 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10088 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10089 rect = g_malloc(sizeof(Rect));
10092 rect->center = *cur_coord;
10093 rects_to_download = g_list_prepend(rects_to_download, rect);
10098 gboolean found = FALSE;
10099 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10100 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10111 GList *fillins = NULL;
10112 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10113 /* seems that ATM the function get_next_coord works only for LATLON */
10114 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10115 /* fill-ins for far apart points */
10116 GList *cur_rect, *next_rect;
10117 for (cur_rect = rects_to_download;
10118 (next_rect = cur_rect->next) != NULL;
10119 cur_rect = cur_rect->next) {
10120 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10121 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10122 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10126 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10129 GList *iter = fillins;
10131 cur_coord = (VikCoord *)(iter->data);
10132 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10133 rect = g_malloc(sizeof(Rect));
10136 rect->center = *cur_coord;
10137 rects_to_download = g_list_prepend(rects_to_download, rect);
10142 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10143 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10147 for (iter = fillins; iter; iter = iter->next)
10148 g_free(iter->data);
10149 g_list_free(fillins);
10151 if (rects_to_download) {
10152 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10153 g_free(rect_iter->data);
10154 g_list_free(rects_to_download);
10158 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10162 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10163 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10164 gint selected_zoom, default_zoom;
10166 VikTrwLayer *vtl = pass_along[0];
10167 VikLayersPanel *vlp = pass_along[1];
10169 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10170 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10172 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10176 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10178 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10179 int num_maps = g_list_length(vmls);
10182 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10186 // Convert from list of vmls to list of names. Allowing the user to select one of them
10187 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10188 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10190 gchar **np = map_names;
10191 VikMapsLayer **lp = map_layers;
10193 for (i = 0; i < num_maps; i++) {
10194 vml = (VikMapsLayer *)(vmls->data);
10196 *np++ = vik_maps_layer_get_map_label(vml);
10199 // Mark end of the array lists
10203 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10204 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10205 if (cur_zoom == zoom_vals[default_zoom])
10208 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10210 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10213 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10216 for (i = 0; i < num_maps; i++)
10217 g_free(map_names[i]);
10219 g_free(map_layers);
10225 /**** lowest waypoint number calculation ***/
10226 static gint highest_wp_number_name_to_number(const gchar *name) {
10227 if ( strlen(name) == 3 ) {
10228 int n = atoi(name);
10229 if ( n < 100 && name[0] != '0' )
10231 if ( n < 10 && name[0] != '0' )
10239 static void highest_wp_number_reset(VikTrwLayer *vtl)
10241 vtl->highest_wp_number = -1;
10244 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10246 /* if is bigger that top, add it */
10247 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10248 if ( new_wp_num > vtl->highest_wp_number )
10249 vtl->highest_wp_number = new_wp_num;
10252 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10254 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10255 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10256 if ( vtl->highest_wp_number == old_wp_num ) {
10258 vtl->highest_wp_number--;
10260 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10261 /* search down until we find something that *does* exist */
10263 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10264 vtl->highest_wp_number--;
10265 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10270 /* get lowest unused number */
10271 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10274 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10276 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10277 return g_strdup(buf);
10281 * trw_layer_create_track_list_both:
10283 * Create the latest list of tracks and routes
10285 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10287 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10288 GList *tracks = NULL;
10289 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10290 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10292 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10295 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] )
10297 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
10299 gchar *title = NULL;
10300 if ( GPOINTER_TO_INT(pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10301 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10303 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10305 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), pass_along[2], trw_layer_create_track_list, FALSE );
10309 static void trw_layer_track_list_dialog ( gpointer lav[2] )
10311 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10312 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10314 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10315 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10319 static void trw_layer_waypoint_list_dialog ( gpointer lav[2] )
10321 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10322 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10324 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10325 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );