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_change_param ( GtkWidget *widget, ui_change_values values );
666 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
667 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
668 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
669 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
670 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
671 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
672 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
673 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
674 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
675 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
676 /* End Layer Interface function definitions */
678 VikLayerInterface vik_trw_layer_interface = {
685 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
689 params_groups, /* params_groups */
690 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
694 (VikLayerFuncCreate) trw_layer_create,
695 (VikLayerFuncRealize) trw_layer_realize,
696 (VikLayerFuncPostRead) trw_layer_post_read,
697 (VikLayerFuncFree) trw_layer_free,
699 (VikLayerFuncProperties) NULL,
700 (VikLayerFuncDraw) trw_layer_draw,
701 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
703 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
704 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
706 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
707 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
709 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
710 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
711 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
712 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
713 (VikLayerFuncLayerSelected) trw_layer_selected,
715 (VikLayerFuncMarshall) trw_layer_marshall,
716 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
718 (VikLayerFuncSetParam) trw_layer_set_param,
719 (VikLayerFuncGetParam) trw_layer_get_param,
720 (VikLayerFuncChangeParam) trw_layer_change_param,
722 (VikLayerFuncReadFileData) a_gpspoint_read_file,
723 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
725 (VikLayerFuncDeleteItem) trw_layer_del_item,
726 (VikLayerFuncCutItem) trw_layer_cut_item,
727 (VikLayerFuncCopyItem) trw_layer_copy_item,
728 (VikLayerFuncPasteItem) trw_layer_paste_item,
729 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
731 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
733 (VikLayerFuncSelectClick) trw_layer_select_click,
734 (VikLayerFuncSelectMove) trw_layer_select_move,
735 (VikLayerFuncSelectRelease) trw_layer_select_release,
736 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
739 GType vik_trw_layer_get_type ()
741 static GType vtl_type = 0;
745 static const GTypeInfo vtl_info =
747 sizeof (VikTrwLayerClass),
748 NULL, /* base_init */
749 NULL, /* base_finalize */
750 NULL, /* class init */
751 NULL, /* class_finalize */
752 NULL, /* class_data */
753 sizeof (VikTrwLayer),
755 NULL /* instance init */
757 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
763 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
765 static gpointer pass_along[6];
771 pass_along[1] = NULL;
772 pass_along[2] = GINT_TO_POINTER (subtype);
773 pass_along[3] = sublayer;
774 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
775 pass_along[5] = NULL;
777 trw_layer_delete_item ( pass_along );
780 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
782 static gpointer pass_along[6];
788 pass_along[1] = NULL;
789 pass_along[2] = GINT_TO_POINTER (subtype);
790 pass_along[3] = sublayer;
791 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
792 pass_along[5] = NULL;
794 trw_layer_copy_item_cb(pass_along);
795 trw_layer_cut_item_cb(pass_along);
798 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
800 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
801 gint subtype = GPOINTER_TO_INT (pass_along[2]);
802 gpointer * sublayer = pass_along[3];
806 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
810 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
811 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
812 if ( wp && wp->name )
815 name = NULL; // Broken :(
817 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
818 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
819 if ( trk && trk->name )
822 name = NULL; // Broken :(
825 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
826 if ( trk && trk->name )
829 name = NULL; // Broken :(
832 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
833 subtype, len, name, data);
837 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
839 trw_layer_copy_item_cb(pass_along);
840 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
841 trw_layer_delete_item(pass_along);
844 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
846 // Slightly cheating method, routing via the panels capability
847 a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
850 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
860 GByteArray *ba = g_byte_array_new ();
862 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
863 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
864 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
865 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
867 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
870 g_byte_array_append ( ba, id, il );
878 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
885 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
889 w = vik_waypoint_unmarshall ( item, len );
890 // When copying - we'll create a new name based on the original
891 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
892 vik_trw_layer_add_waypoint ( vtl, name, w );
893 waypoint_convert (NULL, w, &vtl->coord_mode);
896 trw_layer_calculate_bounds_waypoints ( vtl );
898 // Consider if redraw necessary for the new item
899 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
900 vik_layer_emit_update ( VIK_LAYER(vtl) );
903 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
907 t = vik_track_unmarshall ( item, len );
908 // When copying - we'll create a new name based on the original
909 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
910 vik_trw_layer_add_track ( vtl, name, t );
911 vik_track_convert (t, vtl->coord_mode);
914 // Consider if redraw necessary for the new item
915 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
916 vik_layer_emit_update ( VIK_LAYER(vtl) );
919 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
923 t = vik_track_unmarshall ( item, len );
924 // When copying - we'll create a new name based on the original
925 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
926 vik_trw_layer_add_route ( vtl, name, t );
927 vik_track_convert (t, vtl->coord_mode);
930 // Consider if redraw necessary for the new item
931 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
932 vik_layer_emit_update ( VIK_LAYER(vtl) );
938 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
945 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
949 case PARAM_TV: vtl->tracks_visible = data.b; break;
950 case PARAM_WV: vtl->waypoints_visible = data.b; break;
951 case PARAM_RV: vtl->routes_visible = data.b; break;
952 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
953 case PARAM_TLFONTSIZE:
954 if ( data.u < FS_NUM_SIZES ) {
955 vtl->track_font_size = data.u;
956 g_free ( vtl->track_fsize_str );
957 switch ( vtl->track_font_size ) {
958 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
959 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
960 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
961 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
962 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
963 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
964 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
968 case PARAM_DM: vtl->drawmode = data.u; break;
970 vtl->track_color = data.c;
971 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
973 case PARAM_DP: vtl->drawpoints = data.b; break;
975 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
976 vtl->drawpoints_size = data.u;
978 case PARAM_DE: vtl->drawelevation = data.b; break;
979 case PARAM_DS: vtl->drawstops = data.b; break;
980 case PARAM_DL: vtl->drawlines = data.b; break;
981 case PARAM_DD: vtl->drawdirections = data.b; break;
983 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
984 vtl->drawdirections_size = data.u;
986 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
987 vtl->stop_length = data.u;
989 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
990 vtl->elevation_factor = data.u;
992 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
994 vtl->line_thickness = data.u;
995 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
998 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1000 vtl->bg_line_thickness = data.u;
1001 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1005 vtl->track_bg_color = data.c;
1006 if ( vtl->track_bg_gc )
1007 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1009 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1010 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1011 case PARAM_DLA: vtl->drawlabels = data.b; break;
1012 case PARAM_DI: vtl->drawimages = data.b; break;
1013 case PARAM_IS: if ( data.u != vtl->image_size )
1015 vtl->image_size = data.u;
1016 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1017 g_queue_free ( vtl->image_cache );
1018 vtl->image_cache = g_queue_new ();
1021 case PARAM_IA: vtl->image_alpha = data.u; break;
1022 case PARAM_ICS: vtl->image_cache_size = data.u;
1023 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1024 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1027 vtl->waypoint_color = data.c;
1028 if ( vtl->waypoint_gc )
1029 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1032 vtl->waypoint_text_color = data.c;
1033 if ( vtl->waypoint_text_gc )
1034 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1037 vtl->waypoint_bg_color = data.c;
1038 if ( vtl->waypoint_bg_gc )
1039 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1042 vtl->wpbgand = data.b;
1043 if ( vtl->waypoint_bg_gc )
1044 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1046 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1047 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1048 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1049 case PARAM_WPFONTSIZE:
1050 if ( data.u < FS_NUM_SIZES ) {
1051 vtl->wp_font_size = data.u;
1052 g_free ( vtl->wp_fsize_str );
1053 switch ( vtl->wp_font_size ) {
1054 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1055 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1056 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1057 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1058 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1059 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1060 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1064 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1069 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1071 VikLayerParamData rv;
1074 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1075 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1076 case PARAM_RV: rv.b = vtl->routes_visible; break;
1077 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1078 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1079 case PARAM_DM: rv.u = vtl->drawmode; break;
1080 case PARAM_TC: rv.c = vtl->track_color; break;
1081 case PARAM_DP: rv.b = vtl->drawpoints; break;
1082 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1083 case PARAM_DE: rv.b = vtl->drawelevation; break;
1084 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1085 case PARAM_DS: rv.b = vtl->drawstops; break;
1086 case PARAM_SL: rv.u = vtl->stop_length; break;
1087 case PARAM_DL: rv.b = vtl->drawlines; break;
1088 case PARAM_DD: rv.b = vtl->drawdirections; break;
1089 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1090 case PARAM_LT: rv.u = vtl->line_thickness; break;
1091 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1092 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1093 case PARAM_DI: rv.b = vtl->drawimages; break;
1094 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1095 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1096 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1097 case PARAM_IS: rv.u = vtl->image_size; break;
1098 case PARAM_IA: rv.u = vtl->image_alpha; break;
1099 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1100 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1101 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1102 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1103 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1104 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1105 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1106 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1107 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1108 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1113 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1115 // This '-3' is to account for the first few parameters not in the properties
1116 const gint OFFSET = -3;
1118 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1119 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1122 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1123 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1124 GtkWidget **ww2 = values[UI_CHG_LABELS];
1125 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1126 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1127 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1128 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1129 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1130 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1131 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1132 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1133 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1134 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1135 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1136 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1139 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1142 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1143 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1144 GtkWidget **ww2 = values[UI_CHG_LABELS];
1145 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1146 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1147 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1148 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1149 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1150 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1151 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1152 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1153 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1154 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1155 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1156 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1157 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1158 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1159 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1160 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1163 // Alter sensitivity of all track colours according to the draw track mode.
1166 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1167 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1168 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1169 GtkWidget **ww2 = values[UI_CHG_LABELS];
1170 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1171 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1172 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1173 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1176 // NB Since other track settings have been split across tabs,
1177 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1182 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1189 // Use byte arrays to store sublayer data
1190 // much like done elsewhere e.g. vik_layer_marshall_params()
1191 GByteArray *ba = g_byte_array_new ( );
1196 guint object_length;
1199 // the length of the item
1200 // the sublayer type of item
1201 // the the actual item
1202 #define tlm_append(object_pointer, size, type) \
1204 object_length = (size); \
1205 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1206 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1207 g_byte_array_append ( ba, (object_pointer), object_length );
1209 // Layer parameters first
1210 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1211 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1212 g_byte_array_append ( ba, pd, pl );
1215 // Now sublayer data
1216 GHashTableIter iter;
1217 gpointer key, value;
1220 g_hash_table_iter_init ( &iter, vtl->waypoints );
1221 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1222 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1223 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1228 g_hash_table_iter_init ( &iter, vtl->tracks );
1229 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1230 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1231 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1236 g_hash_table_iter_init ( &iter, vtl->routes );
1237 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1238 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1239 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1249 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1251 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1253 gint consumed_length;
1255 // First the overall layer parameters
1256 memcpy(&pl, data, sizeof(pl));
1258 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1261 consumed_length = pl;
1262 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1264 #define tlm_size (*(gint *)data)
1265 // See marshalling above for order of how this is written
1267 data += sizeof_len_and_subtype + tlm_size;
1269 // Now the individual sublayers:
1271 while ( *data && consumed_length < len ) {
1272 // Normally four extra bytes at the end of the datastream
1273 // (since it's a GByteArray and that's where it's length is stored)
1274 // So only attempt read when there's an actual block of sublayer data
1275 if ( consumed_length + tlm_size < len ) {
1277 // Reuse pl to read the subtype from the data stream
1278 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1280 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1281 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1282 gchar *name = g_strdup ( trk->name );
1283 vik_trw_layer_add_track ( vtl, name, trk );
1286 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1287 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1288 gchar *name = g_strdup ( wp->name );
1289 vik_trw_layer_add_waypoint ( vtl, name, wp );
1292 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1293 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1294 gchar *name = g_strdup ( trk->name );
1295 vik_trw_layer_add_route ( vtl, name, trk );
1299 consumed_length += tlm_size + sizeof_len_and_subtype;
1302 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1304 // Not stored anywhere else so need to regenerate
1305 trw_layer_calculate_bounds_waypoints ( vtl );
1310 // Keep interesting hash function at least visible
1312 static guint strcase_hash(gconstpointer v)
1314 // 31 bit hash function
1317 gchar s[128]; // malloc is too slow for reading big files
1320 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1321 p[i] = toupper(t[i]);
1327 for (p += 1; *p != '\0'; p++)
1328 h = (h << 5) - h + *p;
1335 // Stick a 1 at the end of the function name to make it more unique
1336 // thus more easily searchable in a simple text editor
1337 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1339 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1340 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1342 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1343 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1345 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1346 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1347 // and with normal PC processing capabilities - it has negligibile performance impact
1348 // This also minimized the amount of rework - as the management of the hash tables already exists.
1350 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1351 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1352 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1354 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1355 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1356 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1357 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1358 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1359 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1361 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1363 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1365 // Param settings that are not available via the GUI
1366 // Force to on after processing params (which defaults them to off with a zero value)
1367 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1369 rv->draw_sync_done = TRUE;
1370 rv->draw_sync_do = TRUE;
1371 // Everything else is 0, FALSE or NULL
1377 static void trw_layer_free ( VikTrwLayer *trwlayer )
1379 g_hash_table_destroy(trwlayer->waypoints);
1380 g_hash_table_destroy(trwlayer->waypoints_iters);
1381 g_hash_table_destroy(trwlayer->tracks);
1382 g_hash_table_destroy(trwlayer->tracks_iters);
1383 g_hash_table_destroy(trwlayer->routes);
1384 g_hash_table_destroy(trwlayer->routes_iters);
1386 /* ODC: replace with GArray */
1387 trw_layer_free_track_gcs ( trwlayer );
1389 if ( trwlayer->wp_right_click_menu )
1390 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1392 if ( trwlayer->track_right_click_menu )
1393 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1395 if ( trwlayer->tracklabellayout != NULL)
1396 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1398 if ( trwlayer->wplabellayout != NULL)
1399 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1401 if ( trwlayer->waypoint_gc != NULL )
1402 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1404 if ( trwlayer->waypoint_text_gc != NULL )
1405 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1407 if ( trwlayer->waypoint_bg_gc != NULL )
1408 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1410 g_free ( trwlayer->wp_fsize_str );
1411 g_free ( trwlayer->track_fsize_str );
1413 if ( trwlayer->tpwin != NULL )
1414 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1416 if ( trwlayer->tracks_analysis_dialog != NULL )
1417 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1419 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1420 g_queue_free ( trwlayer->image_cache );
1423 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1427 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1428 dp->xmpp = vik_viewport_get_xmpp ( vp );
1429 dp->ympp = vik_viewport_get_ympp ( vp );
1430 dp->width = vik_viewport_get_width ( vp );
1431 dp->height = vik_viewport_get_height ( vp );
1432 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1433 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1435 dp->center = vik_viewport_get_center ( vp );
1436 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1437 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1442 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1443 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1444 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1446 dp->ce1 = dp->center->east_west-w2;
1447 dp->ce2 = dp->center->east_west+w2;
1448 dp->cn1 = dp->center->north_south-h2;
1449 dp->cn2 = dp->center->north_south+h2;
1450 } else if ( dp->lat_lon ) {
1451 VikCoord upperleft, bottomright;
1452 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1453 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1454 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1455 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1456 dp->ce1 = upperleft.east_west;
1457 dp->ce2 = bottomright.east_west;
1458 dp->cn1 = bottomright.north_south;
1459 dp->cn2 = upperleft.north_south;
1462 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1466 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1467 * Here a simple traffic like light colour system is used:
1468 * . slow points are red
1469 * . average is yellow
1470 * . fast points are green
1472 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1475 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1476 if ( average_speed > 0 ) {
1477 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1478 if ( rv < low_speed )
1479 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1480 else if ( rv > high_speed )
1481 return VIK_TRW_LAYER_TRACK_GC_FAST;
1483 return VIK_TRW_LAYER_TRACK_GC_AVER;
1486 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1489 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1491 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1492 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1493 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1494 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1498 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1500 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1502 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1503 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1505 // Fallback if parse failure
1506 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1508 g_free ( label_markup );
1510 gint label_x, label_y;
1512 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1514 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1515 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1519 * distance_in_preferred_units:
1520 * @dist: The source distance in standard SI Units (i.e. metres)
1522 * TODO: This is a generic function that could be moved into globals.c or utils.c
1524 * Probably best used if you have a only few conversions to perform.
1525 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1526 * since it will be doing the preference check on each call
1528 * Returns: The distance in the units as specified by the preferences
1530 static gdouble distance_in_preferred_units ( gdouble dist )
1533 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1534 switch (dist_units) {
1535 case VIK_UNITS_DISTANCE_MILES:
1536 mydist = VIK_METERS_TO_MILES(dist);
1538 // VIK_UNITS_DISTANCE_KILOMETRES:
1540 mydist = dist/1000.0;
1547 * trw_layer_draw_dist_labels:
1549 * Draw a few labels along a track at nicely seperated distances
1550 * This might slow things down if there's many tracks being displayed with this on.
1552 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1554 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1555 25.0, 40.0, 50.0, 75.0, 100.0,
1556 150.0, 200.0, 250.0, 500.0, 1000.0};
1558 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1560 // Convert to specified unit to find the friendly breakdown value
1561 dist = distance_in_preferred_units ( dist );
1565 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1566 if ( chunksd[i] > dist ) {
1568 dist = chunksd[index];
1573 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1575 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1576 gdouble dist_i = dist * i;
1578 // Convert distance back into metres for use in finding a trackpoint
1579 switch (dist_units) {
1580 case VIK_UNITS_DISTANCE_MILES:
1581 dist_i = VIK_MILES_TO_METERS(dist_i);
1583 // VIK_UNITS_DISTANCE_KILOMETRES:
1585 dist_i = dist_i*1000.0;
1589 gdouble dist_current = 0.0;
1590 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1591 gdouble dist_next = 0.0;
1592 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1594 gdouble dist_between_tps = fabs (dist_next - dist_current);
1595 gdouble ratio = 0.0;
1596 // Prevent division by 0 errors
1597 if ( dist_between_tps > 0.0 )
1598 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1600 if ( tp_current && tp_next ) {
1601 // Construct the name based on the distance value
1604 switch (dist_units) {
1605 case VIK_UNITS_DISTANCE_MILES:
1606 units = g_strdup ( _("miles") );
1608 // VIK_UNITS_DISTANCE_KILOMETRES:
1610 units = g_strdup ( _("km") );
1614 // Convert for display
1615 dist_i = distance_in_preferred_units ( dist_i );
1617 // Make the precision of the output related to the unit size.
1619 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1620 else if ( index == 1 )
1621 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1623 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1626 struct LatLon ll_current, ll_next;
1627 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1628 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1630 // positional interpolation
1631 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1632 // but should be good enough over the small scale that I anticipate usage on
1633 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1634 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1636 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1639 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1640 fgcolour = gdk_color_to_string ( &(trk->color) );
1642 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1644 // if highlight mode on, then colour the background in the highlight colour
1646 if ( drawing_highlight )
1647 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1649 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1651 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1653 g_free ( fgcolour );
1654 g_free ( bgcolour );
1661 * trw_layer_draw_track_name_labels:
1663 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1665 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1668 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1669 fgcolour = gdk_color_to_string ( &(trk->color) );
1671 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1673 // if highlight mode on, then colour the background in the highlight colour
1675 if ( drawing_highlight )
1676 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1678 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1680 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1682 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1683 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1684 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1685 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1686 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1687 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1689 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1691 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1694 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1695 // No other labels to draw
1698 GList *ending = g_list_last ( trk->trackpoints );
1699 VikCoord begin_coord = VIK_TRACKPOINT(trk->trackpoints->data)->coord;
1700 VikCoord end_coord = VIK_TRACKPOINT(ending->data)->coord;
1702 gboolean done_start_end = FALSE;
1704 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1705 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1707 // This number can be configured via the settings if you really want to change it
1708 gdouble distance_diff;
1709 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1710 distance_diff = 100.0; // Metres
1712 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1713 // Start and end 'close' together so only draw one label at an average location
1714 gint x1, x2, y1, y2;
1715 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1716 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1718 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1720 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1721 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1724 done_start_end = TRUE;
1728 if ( ! done_start_end ) {
1729 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1730 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1731 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1732 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1733 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1734 g_free ( name_start );
1736 // Don't draw end label if this is the one being created
1737 if ( trk != dp->vtl->current_track ) {
1738 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1739 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1740 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1741 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1742 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1743 g_free ( name_end );
1748 g_free ( fgcolour );
1749 g_free ( bgcolour );
1753 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1755 if ( ! track->visible )
1758 /* TODO: this function is a mess, get rid of any redundancy */
1759 GList *list = track->trackpoints;
1761 gboolean useoldvals = TRUE;
1763 gboolean drawpoints;
1765 gboolean drawelevation;
1766 gdouble min_alt, max_alt, alt_diff = 0;
1768 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1769 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1772 if ( dp->vtl->drawelevation )
1774 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1775 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1776 alt_diff = max_alt - min_alt;
1779 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1780 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1781 trw_layer_draw_track ( id, track, dp, TRUE );
1783 if ( draw_track_outline )
1784 drawpoints = drawstops = FALSE;
1786 drawpoints = dp->vtl->drawpoints;
1787 drawstops = dp->vtl->drawstops;
1790 gboolean drawing_highlight = FALSE;
1791 /* Current track - used for creation */
1792 if ( track == dp->vtl->current_track )
1793 main_gc = dp->vtl->current_track_gc;
1795 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1796 /* Draw all tracks of the layer in special colour */
1797 /* if track is member of selected layer or is the current selected track
1798 then draw in the highlight colour.
1799 NB this supercedes the drawmode */
1800 if ( ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ) ||
1801 ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1802 ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( dp->vw ) ) ) ||
1803 ( track == vik_window_get_selected_track ( dp->vw ) ) ) {
1804 main_gc = vik_viewport_get_gc_highlight (dp->vp);
1805 drawing_highlight = TRUE;
1808 if ( !drawing_highlight ) {
1809 // Still need to figure out the gc according to the drawing mode:
1810 switch ( dp->vtl->drawmode ) {
1811 case DRAWMODE_BY_TRACK:
1812 if ( dp->vtl->track_1color_gc )
1813 g_object_unref ( dp->vtl->track_1color_gc );
1814 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1815 main_gc = dp->vtl->track_1color_gc;
1818 // Mostly for DRAWMODE_ALL_SAME_COLOR
1819 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1820 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
1827 int x, y, oldx, oldy;
1828 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1830 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1832 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1834 // Draw the first point as something a bit different from the normal points
1835 // ATM it's slightly bigger and a triangle
1837 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1838 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1844 gdouble average_speed = 0.0;
1845 gdouble low_speed = 0.0;
1846 gdouble high_speed = 0.0;
1847 // If necessary calculate these values - which is done only once per track redraw
1848 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1849 // the percentage factor away from the average speed determines transistions between the levels
1850 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1851 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1852 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1855 while ((list = g_list_next(list)))
1857 tp = VIK_TRACKPOINT(list->data);
1858 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1860 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1861 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1862 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1863 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1864 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1866 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1869 * If points are the same in display coordinates, don't draw.
1871 if ( useoldvals && x == oldx && y == oldy )
1873 // Still need to process points to ensure 'stops' are drawn if required
1874 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1875 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1876 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 );
1881 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1882 if ( drawpoints || dp->vtl->drawlines ) {
1883 // setup main_gc for both point and line drawing
1884 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1885 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 ) );
1889 if ( drawpoints && ! draw_track_outline )
1894 * The concept of drawing stops is that a trackpoint
1895 * that is if the next trackpoint has a timestamp far into
1896 * the future, we draw a circle of 6x trackpoint size,
1897 * instead of a rectangle of 2x trackpoint size.
1898 * This is drawn first so the trackpoint will be drawn on top
1901 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1902 /* Stop point. Draw 6x circle. Always in redish colour */
1903 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 );
1905 /* Regular point - draw 2x square. */
1906 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1909 /* Final point - draw 4x circle. */
1910 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 );
1913 if ((!tp->newsegment) && (dp->vtl->drawlines))
1916 /* UTM only: zone check */
1917 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1918 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
1921 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1923 if ( draw_track_outline ) {
1924 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1928 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1930 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1932 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1937 tmp[1].y = oldy-FIXALTITUDE(list->data);
1939 tmp[2].y = y-FIXALTITUDE(list->next->data);
1944 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1945 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
1947 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
1948 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1950 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1955 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1956 // Draw an arrow at the mid point to show the direction of the track
1957 // Code is a rework from vikwindow::draw_ruler()
1958 gint midx = (oldx + x) / 2;
1959 gint midy = (oldy + y) / 2;
1961 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1962 // Avoid divide by zero and ensure at least 1 pixel big
1964 gdouble dx = (oldx - midx) / len;
1965 gdouble dy = (oldy - midy) / len;
1966 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1967 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1977 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1979 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1980 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1982 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1984 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1985 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 ));
1989 * If points are the same in display coordinates, don't draw.
1991 if ( x != oldx || y != oldy )
1993 if ( draw_track_outline )
1994 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1996 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2002 * If points are the same in display coordinates, don't draw.
2004 if ( x != oldx && y != oldy )
2006 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2007 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2015 // Labels drawn after the trackpoints, so the labels are on top
2016 if ( dp->vtl->track_draw_labels ) {
2017 if ( track->max_number_dist_labels > 0 ) {
2018 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2021 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2022 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2028 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2030 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2031 trw_layer_draw_track ( id, track, dp, FALSE );
2035 static void cached_pixbuf_free ( CachedPixbuf *cp )
2037 g_object_unref ( G_OBJECT(cp->pixbuf) );
2038 g_free ( cp->image );
2041 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2043 return strcmp ( cp->image, name );
2046 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2049 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2050 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2051 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2054 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2056 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2058 if ( wp->image && dp->vtl->drawimages )
2060 GdkPixbuf *pixbuf = NULL;
2063 if ( dp->vtl->image_alpha == 0)
2066 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2068 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2071 gchar *image = wp->image;
2072 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2073 if ( ! regularthumb )
2075 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2076 image = "\x12\x00"; /* this shouldn't occur naturally. */
2080 CachedPixbuf *cp = NULL;
2081 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2082 if ( dp->vtl->image_size == 128 )
2083 cp->pixbuf = regularthumb;
2086 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2087 g_assert ( cp->pixbuf );
2088 g_object_unref ( G_OBJECT(regularthumb) );
2090 cp->image = g_strdup ( image );
2092 /* needed so 'click picture' tool knows how big the pic is; we don't
2093 * store it in cp because they may have been freed already. */
2094 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2095 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2097 g_queue_push_head ( dp->vtl->image_cache, cp );
2098 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2099 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2101 pixbuf = cp->pixbuf;
2105 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2111 w = gdk_pixbuf_get_width ( pixbuf );
2112 h = gdk_pixbuf_get_height ( pixbuf );
2114 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2116 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2117 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2118 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2119 wp == vik_window_get_selected_waypoint ( dp->vw ) ) {
2120 // Highlighted - so draw a little border around the chosen one
2121 // single line seems a little weak so draw 2 of them
2122 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2123 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2124 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2125 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2128 if ( dp->vtl->image_alpha == 255 )
2129 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2131 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2133 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2137 // Draw appropriate symbol - either symbol image or simple types
2138 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2139 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 );
2141 else if ( wp == dp->vtl->current_wp ) {
2142 switch ( dp->vtl->wp_symbol ) {
2143 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;
2144 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;
2145 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;
2146 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 );
2147 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 );
2151 switch ( dp->vtl->wp_symbol ) {
2152 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;
2153 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;
2154 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;
2155 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 );
2156 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;
2160 if ( dp->vtl->drawlabels )
2162 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2163 gint label_x, label_y;
2165 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2167 // Could this stored in the waypoint rather than recreating each pass?
2168 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2170 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2171 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2173 // Fallback if parse failure
2174 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2176 g_free ( wp_label_markup );
2178 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2179 label_x = x - width/2;
2180 if ( wp->symbol_pixbuf )
2181 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2183 label_y = y - dp->vtl->wp_size - height - 2;
2185 /* if highlight mode on, then draw background text in highlight colour */
2186 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
2187 if ( dp->vtl == vik_window_get_selected_trw_layer ( dp->vw ) ||
2188 dp->vtl->waypoints == vik_window_get_selected_waypoints ( dp->vw ) ||
2189 wp == vik_window_get_selected_waypoint ( dp->vw ) )
2190 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2192 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2195 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2197 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2202 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2204 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2205 trw_layer_draw_waypoint ( id, wp, dp );
2209 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2211 static struct DrawingParams dp;
2212 g_assert ( l != NULL );
2214 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
2216 if ( l->tracks_visible )
2217 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2219 if ( l->routes_visible )
2220 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2222 if (l->waypoints_visible)
2223 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2226 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2229 if ( vtl->track_bg_gc )
2231 g_object_unref ( vtl->track_bg_gc );
2232 vtl->track_bg_gc = NULL;
2234 if ( vtl->track_1color_gc )
2236 g_object_unref ( vtl->track_1color_gc );
2237 vtl->track_1color_gc = NULL;
2239 if ( vtl->current_track_gc )
2241 g_object_unref ( vtl->current_track_gc );
2242 vtl->current_track_gc = NULL;
2244 if ( vtl->current_track_newpoint_gc )
2246 g_object_unref ( vtl->current_track_newpoint_gc );
2247 vtl->current_track_newpoint_gc = NULL;
2250 if ( ! vtl->track_gc )
2252 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2253 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2254 g_array_free ( vtl->track_gc, TRUE );
2255 vtl->track_gc = NULL;
2258 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2260 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2261 gint width = vtl->line_thickness;
2263 if ( vtl->track_gc )
2264 trw_layer_free_track_gcs ( vtl );
2266 if ( vtl->track_bg_gc )
2267 g_object_unref ( vtl->track_bg_gc );
2268 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2270 // Ensure new track drawing heeds line thickness setting
2271 // however always have a minium of 2, as 1 pixel is really narrow
2272 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2274 if ( vtl->current_track_gc )
2275 g_object_unref ( vtl->current_track_gc );
2276 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2277 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2279 // 'newpoint' gc is exactly the same as the current track gc
2280 if ( vtl->current_track_newpoint_gc )
2281 g_object_unref ( vtl->current_track_newpoint_gc );
2282 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2283 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2285 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2287 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2288 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2290 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2291 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2292 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2294 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2296 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2299 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2301 VikTrwLayer *rv = trw_layer_new1 ( vp );
2302 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2304 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2305 /* early exit, as the rest is GUI related */
2309 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2310 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2312 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2313 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2315 trw_layer_new_track_gcs ( rv, vp );
2317 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2318 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2319 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2320 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2322 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2324 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2329 #define SMALL_ICON_SIZE 18
2331 * Can accept a null symbol, and may return null value
2333 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2335 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2336 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2337 // So needing a small icon for the treeview may need some resizing:
2338 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2339 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2343 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2345 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2347 GdkPixbuf *pixbuf = NULL;
2349 if ( track->has_color ) {
2350 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2351 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2352 // Here is some magic found to do the conversion
2353 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2354 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2355 ((track->color.green & 0xff00) << 8) |
2356 (track->color.blue & 0xff00);
2358 gdk_pixbuf_fill ( pixbuf, pixel );
2361 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 );
2364 g_object_unref (pixbuf);
2366 *new_iter = *((GtkTreeIter *) pass_along[1]);
2367 if ( track->is_route )
2368 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2370 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2372 if ( ! track->visible )
2373 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2376 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2378 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2380 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 );
2382 *new_iter = *((GtkTreeIter *) pass_along[1]);
2383 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2385 if ( ! wp->visible )
2386 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2389 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2391 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2394 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2396 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2399 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2401 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2404 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2407 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2409 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2410 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2412 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2414 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2417 if ( g_hash_table_size (vtl->routes) > 0 ) {
2418 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2420 pass_along[0] = &(vtl->routes_iter);
2421 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2423 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2425 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2428 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2429 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2431 pass_along[0] = &(vtl->waypoints_iter);
2432 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2434 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2436 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2441 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2445 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2446 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2447 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2448 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2450 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2452 return (t->visible ^= 1);
2456 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2458 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2460 return (t->visible ^= 1);
2464 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2466 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2468 return (t->visible ^= 1);
2477 * Return a property about tracks for this layer
2479 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2481 return vtl->line_thickness;
2484 // Structure to hold multiple track information for a layer
2493 * Build up layer multiple track information via updating the tooltip_tracks structure
2495 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2497 tt->length = tt->length + vik_track_get_length (tr);
2499 // Ensure times are available
2500 if ( tr->trackpoints &&
2501 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2502 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2505 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2506 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2508 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2509 // Hence initialize to the first 'proper' value
2510 if ( tt->start_time == 0 )
2511 tt->start_time = t1;
2512 if ( tt->end_time == 0 )
2515 // Update find the earliest / last times
2516 if ( t1 < tt->start_time )
2517 tt->start_time = t1;
2518 if ( t2 > tt->end_time )
2521 // Keep track of total time
2522 // there maybe gaps within a track (eg segments)
2523 // but this should be generally good enough for a simple indicator
2524 tt->duration = tt->duration + (int)(t2-t1);
2529 * Generate tooltip text for the layer.
2530 * This is relatively complicated as it considers information for
2531 * no tracks, a single track or multiple tracks
2532 * (which may or may not have timing information)
2534 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2545 static gchar tmp_buf[128];
2548 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2550 // Safety check - I think these should always be valid
2551 if ( vtl->tracks && vtl->waypoints ) {
2552 tooltip_tracks tt = { 0.0, 0, 0 };
2553 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2555 GDate* gdate_start = g_date_new ();
2556 g_date_set_time_t (gdate_start, tt.start_time);
2558 GDate* gdate_end = g_date_new ();
2559 g_date_set_time_t (gdate_end, tt.end_time);
2561 if ( g_date_compare (gdate_start, gdate_end) ) {
2562 // Dates differ so print range on separate line
2563 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2564 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2565 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2568 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2569 if ( tt.start_time != 0 )
2570 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2574 if ( tt.length > 0.0 ) {
2575 gdouble len_in_units;
2577 // Setup info dependent on distance units
2578 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2579 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2580 len_in_units = VIK_METERS_TO_MILES(tt.length);
2583 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2584 len_in_units = tt.length/1000.0;
2587 // Timing information if available
2589 if ( tt.duration > 0 ) {
2590 g_snprintf (tbuf1, sizeof(tbuf1),
2591 _(" in %d:%02d hrs:mins"),
2592 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2594 g_snprintf (tbuf2, sizeof(tbuf2),
2595 _("\n%sTotal Length %.1f %s%s"),
2596 tbuf3, len_in_units, tbuf4, tbuf1);
2599 // Put together all the elements to form compact tooltip text
2600 g_snprintf (tmp_buf, sizeof(tmp_buf),
2601 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2602 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2604 g_date_free (gdate_start);
2605 g_date_free (gdate_end);
2612 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2616 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2618 // Very simple tooltip - may expand detail in the future...
2619 static gchar tmp_buf[32];
2620 g_snprintf (tmp_buf, sizeof(tmp_buf),
2622 g_hash_table_size (l->tracks));
2626 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2628 // Very simple tooltip - may expand detail in the future...
2629 static gchar tmp_buf[32];
2630 g_snprintf (tmp_buf, sizeof(tmp_buf),
2632 g_hash_table_size (l->routes));
2637 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2638 // Same tooltip for a route
2639 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2642 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2643 tr = g_hash_table_lookup ( l->tracks, sublayer );
2645 tr = g_hash_table_lookup ( l->routes, sublayer );
2648 // Could be a better way of handling strings - but this works...
2649 gchar time_buf1[20];
2650 gchar time_buf2[20];
2651 time_buf1[0] = '\0';
2652 time_buf2[0] = '\0';
2653 static gchar tmp_buf[100];
2654 // Compact info: Short date eg (11/20/99), duration and length
2655 // Hopefully these are the things that are most useful and so promoted into the tooltip
2656 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2657 // %x The preferred date representation for the current locale without the time.
2658 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2659 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2660 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2662 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2665 // Get length and consider the appropriate distance units
2666 gdouble tr_len = vik_track_get_length(tr);
2667 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2668 switch (dist_units) {
2669 case VIK_UNITS_DISTANCE_KILOMETRES:
2670 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2672 case VIK_UNITS_DISTANCE_MILES:
2673 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2682 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2684 // Very simple tooltip - may expand detail in the future...
2685 static gchar tmp_buf[32];
2686 g_snprintf (tmp_buf, sizeof(tmp_buf),
2688 g_hash_table_size (l->waypoints));
2692 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2694 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2695 // NB It's OK to return NULL
2700 return w->description;
2709 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2712 * set_statusbar_msg_info_trkpt:
2714 * Function to show track point information on the statusbar
2715 * Items displayed is controlled by the settings format code
2717 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2719 gchar *statusbar_format_code = NULL;
2720 gboolean need2free = FALSE;
2721 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2722 // Otherwise use default
2723 statusbar_format_code = g_strdup ( "KEATDN" );
2727 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2728 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2732 g_free ( statusbar_format_code );
2736 * Function to show basic waypoint information on the statusbar
2738 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2741 switch (a_vik_get_units_height ()) {
2742 case VIK_UNITS_HEIGHT_FEET:
2743 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2746 //VIK_UNITS_HEIGHT_METRES:
2747 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2751 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2752 // one can easily use the current pointer position to see this if needed
2753 gchar *lat = NULL, *lon = NULL;
2754 static struct LatLon ll;
2755 vik_coord_to_latlon (&(wpt->coord), &ll);
2756 a_coords_latlon_to_string ( &ll, &lat, &lon );
2758 // Combine parts to make overall message
2761 // Add comment if available
2762 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2764 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2765 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2772 * General layer selection function, find out which bit is selected and take appropriate action
2774 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2777 l->current_wp = NULL;
2778 l->current_wp_id = NULL;
2779 trw_layer_cancel_current_tp ( l, FALSE );
2782 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2786 case VIK_TREEVIEW_TYPE_LAYER:
2788 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2789 /* Mark for redraw */
2794 case VIK_TREEVIEW_TYPE_SUBLAYER:
2798 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2800 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2801 /* Mark for redraw */
2805 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2807 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2808 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2809 /* Mark for redraw */
2813 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2815 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2816 /* Mark for redraw */
2820 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2822 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2823 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2824 /* Mark for redraw */
2828 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2830 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2831 /* Mark for redraw */
2835 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2837 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2839 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2840 // Show some waypoint info
2841 set_statusbar_msg_info_wpt ( l, wpt );
2842 /* Mark for redraw */
2849 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2858 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2863 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2868 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2873 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2875 return l->waypoints;
2878 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
2880 return vtl->tracks_iters;
2883 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
2885 return vtl->routes_iters;
2888 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
2890 return vtl->waypoints;
2893 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
2895 return ! ( g_hash_table_size ( vtl->tracks ) ||
2896 g_hash_table_size ( vtl->routes ) ||
2897 g_hash_table_size ( vtl->waypoints ) );
2900 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
2902 return vtl->tracks_visible;
2905 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
2907 return vtl->routes_visible;
2910 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
2912 return vtl->waypoints_visible;
2916 * ATM use a case sensitive find
2917 * Finds the first one
2919 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2921 if ( wp && wp->name )
2922 if ( ! strcmp ( wp->name, name ) )
2928 * Get waypoint by name - not guaranteed to be unique
2929 * Finds the first one
2931 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2933 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2937 * ATM use a case sensitive find
2938 * Finds the first one
2940 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2942 if ( trk && trk->name )
2943 if ( ! strcmp ( trk->name, name ) )
2949 * Get track by name - not guaranteed to be unique
2950 * Finds the first one
2952 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2954 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2958 * Get route by name - not guaranteed to be unique
2959 * Finds the first one
2961 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2963 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2966 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
2968 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
2969 maxmin[0].lat = trk->bbox.north;
2970 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
2971 maxmin[1].lat = trk->bbox.south;
2972 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
2973 maxmin[0].lon = trk->bbox.east;
2974 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
2975 maxmin[1].lon = trk->bbox.west;
2978 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2980 // Continually reuse maxmin to find the latest maximum and minimum values
2981 // First set to waypoints bounds
2982 maxmin[0].lat = vtl->waypoints_bbox.north;
2983 maxmin[1].lat = vtl->waypoints_bbox.south;
2984 maxmin[0].lon = vtl->waypoints_bbox.east;
2985 maxmin[1].lon = vtl->waypoints_bbox.west;
2986 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2987 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2990 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2992 /* 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... */
2993 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2994 trw_layer_find_maxmin (vtl, maxmin);
2995 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2999 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3000 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3005 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
3008 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
3009 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
3011 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3014 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3016 /* First set the center [in case previously viewing from elsewhere] */
3017 /* Then loop through zoom levels until provided positions are in view */
3018 /* This method is not particularly fast - but should work well enough */
3019 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3021 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3022 vik_viewport_set_center_coord ( vvp, &coord );
3024 /* Convert into definite 'smallest' and 'largest' positions */
3025 struct LatLon minmin;
3026 if ( maxmin[0].lat < maxmin[1].lat )
3027 minmin.lat = maxmin[0].lat;
3029 minmin.lat = maxmin[1].lat;
3031 struct LatLon maxmax;
3032 if ( maxmin[0].lon > maxmin[1].lon )
3033 maxmax.lon = maxmin[0].lon;
3035 maxmax.lon = maxmin[1].lon;
3037 /* Never zoom in too far - generally not that useful, as too close ! */
3038 /* Always recalculate the 'best' zoom level */
3040 vik_viewport_set_zoom ( vvp, zoom );
3042 gdouble min_lat, max_lat, min_lon, max_lon;
3043 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3044 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3045 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3046 /* NB I think the logic used in this test to determine if the bounds is within view
3047 fails if track goes across 180 degrees longitude.
3048 Hopefully that situation is not too common...
3049 Mind you viking doesn't really do edge locations to well anyway */
3050 if ( min_lat < minmin.lat &&
3051 max_lat > minmin.lat &&
3052 min_lon < maxmax.lon &&
3053 max_lon > maxmax.lon )
3054 /* Found within zoom level */
3059 vik_viewport_set_zoom ( vvp, zoom );
3063 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3065 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3066 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3067 trw_layer_find_maxmin (vtl, maxmin);
3068 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3071 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3076 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
3078 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])) ) ) {
3079 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3082 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
3085 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
3087 GtkWidget *file_selector;
3089 gboolean failed = FALSE;
3090 file_selector = gtk_file_chooser_dialog_new (title,
3092 GTK_FILE_CHOOSER_ACTION_SAVE,
3093 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3094 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3096 gchar *cwd = g_get_current_dir();
3098 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(file_selector), cwd );
3102 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
3104 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
3106 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
3107 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE ||
3108 a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3110 gtk_widget_hide ( file_selector );
3111 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3112 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
3113 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3117 gtk_widget_destroy ( file_selector );
3119 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
3122 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
3124 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
3127 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
3129 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
3132 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
3134 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3135 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3136 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3137 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3139 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3141 g_free ( auto_save_name );
3144 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
3146 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
3147 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
3148 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
3149 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
3151 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3153 g_free ( auto_save_name );
3157 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
3160 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
3162 gchar *name_used = NULL;
3165 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
3166 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3167 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
3168 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0])) );
3170 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
3174 gchar *quoted_file = g_shell_quote ( name_used );
3175 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
3176 g_free ( quoted_file );
3177 if ( ! g_spawn_command_line_async ( cmd, &err ) )
3179 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
3180 g_error_free ( err );
3184 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
3185 //g_remove ( name_used );
3186 // Perhaps should be deleted when the program ends?
3187 // For now leave it to the user to delete it / use system temp cleanup methods.
3188 g_free ( name_used );
3192 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
3194 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
3197 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
3199 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
3202 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
3204 gpointer layer_and_vlp[2];
3205 layer_and_vlp[0] = pass_along[0];
3206 layer_and_vlp[1] = pass_along[1];
3208 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3210 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3211 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3213 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3215 if ( !trk || !trk->name )
3218 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
3219 gchar *auto_save_name = g_strdup ( trk->name );
3220 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
3221 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
3223 gchar *label = NULL;
3224 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3225 label = _("Export Route as GPX");
3227 label = _("Export Track as GPX");
3228 trw_layer_export ( layer_and_vlp, label, auto_save_name, trk, FILE_TYPE_GPX );
3230 g_free ( auto_save_name );
3233 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3235 wpu_udata *user_data = udata;
3236 if ( wp == user_data->wp ) {
3237 user_data->uuid = id;
3243 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
3245 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3246 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
3247 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3249 GTK_RESPONSE_REJECT,
3251 GTK_RESPONSE_ACCEPT,
3254 GtkWidget *label, *entry;
3255 label = gtk_label_new(_("Waypoint Name:"));
3256 entry = gtk_entry_new();
3258 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3259 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3260 gtk_widget_show_all ( label );
3261 gtk_widget_show_all ( entry );
3263 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3265 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3267 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3268 // Find *first* wp with the given name
3269 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
3272 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
3275 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
3276 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
3278 // Find and select on the side panel
3283 // Hmmm, want key of it
3284 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3286 if ( wpf && udata.uuid ) {
3287 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
3288 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
3297 gtk_widget_destroy ( dia );
3300 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3302 gchar *default_name = highest_wp_number_get(vtl);
3303 VikWaypoint *wp = vik_waypoint_new();
3304 gchar *returned_name;
3306 wp->coord = *def_coord;
3308 // Attempt to auto set height if DEM data is available
3309 vik_waypoint_apply_dem_data ( wp, TRUE );
3311 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3313 if ( returned_name )
3316 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3317 g_free (default_name);
3318 g_free (returned_name);
3321 g_free (default_name);
3322 vik_waypoint_free(wp);
3326 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
3328 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3329 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3330 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3331 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3332 VikViewport *vvp = vik_window_viewport(vw);
3334 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3335 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3336 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3337 trw_layer_calculate_bounds_waypoints ( vtl );
3338 vik_layers_panel_emit_update ( vlp );
3341 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
3343 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3344 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3345 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3347 trw_layer_find_maxmin (vtl, maxmin);
3348 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3349 trw_layer_calculate_bounds_waypoints ( vtl );
3350 vik_layers_panel_emit_update ( vlp );
3353 #ifdef VIK_CONFIG_GEOTAG
3354 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
3356 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3358 // Update directly - not changing the mtime
3359 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3362 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3364 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3367 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3371 * Use code in separate file for this feature as reasonably complex
3373 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3375 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3376 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3377 // Unset so can be reverified later if necessary
3378 vtl->has_verified_thumbnails = FALSE;
3380 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3386 static void trw_layer_geotagging_waypoint ( gpointer pass_along[6] )
3388 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3389 VikWaypoint *wpt = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3391 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3397 static void trw_layer_geotagging ( gpointer lav[2] )
3399 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3400 // Unset so can be reverified later if necessary
3401 vtl->has_verified_thumbnails = FALSE;
3403 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3410 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3412 static void trw_layer_acquire ( gpointer lav[2], VikDataSourceInterface *datasource )
3414 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3415 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3416 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3417 VikViewport *vvp = vik_window_viewport(vw);
3419 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3423 * Acquire into this TRW Layer straight from GPS Device
3425 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3427 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3428 trw_layer_acquire ( lav, &vik_datasource_gps_interface );
3432 * Acquire into this TRW Layer from Directions
3434 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3436 trw_layer_acquire ( lav, &vik_datasource_routing_interface );
3440 * Acquire into this TRW Layer from an entered URL
3442 static void trw_layer_acquire_url_cb ( gpointer lav[2] )
3444 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3445 trw_layer_acquire ( lav, &vik_datasource_url_interface );
3448 #ifdef VIK_CONFIG_OPENSTREETMAP
3450 * Acquire into this TRW Layer from OSM
3452 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3454 trw_layer_acquire ( lav, &vik_datasource_osm_interface );
3458 * Acquire into this TRW Layer from OSM for 'My' Traces
3460 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3462 trw_layer_acquire ( lav, &vik_datasource_osm_my_traces_interface );
3466 #ifdef VIK_CONFIG_GEOCACHES
3468 * Acquire into this TRW Layer from Geocaching.com
3470 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3472 trw_layer_acquire ( lav, &vik_datasource_gc_interface );
3476 #ifdef VIK_CONFIG_GEOTAG
3478 * Acquire into this TRW Layer from images
3480 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3482 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3484 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3485 trw_layer_acquire ( lav, &vik_datasource_geotag_interface );
3487 // Reverify thumbnails as they may have changed
3488 vtl->has_verified_thumbnails = FALSE;
3489 trw_layer_verify_thumbnails ( vtl, NULL );
3493 static void trw_layer_gps_upload ( gpointer lav[2] )
3495 gpointer pass_along[6];
3496 pass_along[0] = lav[0];
3497 pass_along[1] = lav[1];
3498 pass_along[2] = NULL; // No track - operate on the layer
3499 pass_along[3] = NULL;
3500 pass_along[4] = NULL;
3501 pass_along[5] = NULL;
3503 trw_layer_gps_upload_any ( pass_along );
3507 * If pass_along[3] is defined that this will upload just that track
3509 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3511 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3512 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3514 // May not actually get a track here as pass_along[2&3] can be null
3515 VikTrack *track = NULL;
3516 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3517 gboolean xfer_all = FALSE;
3519 if ( pass_along[2] ) {
3521 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3522 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3525 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3526 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3529 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3532 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3536 else if ( !pass_along[4] )
3537 xfer_all = TRUE; // i.e. whole layer
3539 if (track && !track->visible) {
3540 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3544 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3545 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3546 GTK_DIALOG_DESTROY_WITH_PARENT,
3548 GTK_RESPONSE_ACCEPT,
3550 GTK_RESPONSE_REJECT,
3553 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3554 GtkWidget *response_w = NULL;
3555 #if GTK_CHECK_VERSION (2, 20, 0)
3556 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3560 gtk_widget_grab_focus ( response_w );
3562 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3564 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3565 datasource_gps_clean_up ( dgs );
3566 gtk_widget_destroy ( dialog );
3570 // Get info from reused datasource dialog widgets
3571 gchar* protocol = datasource_gps_get_protocol ( dgs );
3572 gchar* port = datasource_gps_get_descriptor ( dgs );
3573 // NB don't free the above strings as they're references to values held elsewhere
3574 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3575 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3576 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3577 gboolean turn_off = datasource_gps_get_off ( dgs );
3579 gtk_widget_destroy ( dialog );
3581 // When called from the viewport - work the corresponding layerspanel:
3583 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3586 // Apply settings to transfer to the GPS device
3593 vik_layers_panel_get_viewport (vlp),
3602 * Acquire into this TRW Layer from any GPS Babel supported file
3604 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3606 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3607 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3608 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3609 VikViewport *vvp = vik_window_viewport(vw);
3611 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3614 static void trw_layer_new_wp ( gpointer lav[2] )
3616 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3617 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3618 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3619 instead return true if you want to update. */
3620 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 ) {
3621 trw_layer_calculate_bounds_waypoints ( vtl );
3622 vik_layers_panel_emit_update ( vlp );
3626 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3628 vtl->current_track = vik_track_new();
3629 vik_track_set_defaults ( vtl->current_track );
3630 vtl->current_track->visible = TRUE;
3631 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3632 // Create track with the preferred colour from the layer properties
3633 vtl->current_track->color = vtl->track_color;
3635 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3636 vtl->current_track->has_color = TRUE;
3637 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3640 static void trw_layer_new_track ( gpointer lav[2] )
3642 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3644 if ( ! vtl->current_track ) {
3645 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3646 new_track_create_common ( vtl, name );
3649 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3653 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3655 vtl->current_track = vik_track_new();
3656 vik_track_set_defaults ( vtl->current_track );
3657 vtl->current_track->visible = TRUE;
3658 vtl->current_track->is_route = TRUE;
3659 // By default make all routes red
3660 vtl->current_track->has_color = TRUE;
3661 gdk_color_parse ( "red", &vtl->current_track->color );
3662 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3665 static void trw_layer_new_route ( gpointer lav[2] )
3667 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3669 if ( ! vtl->current_track ) {
3670 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3671 new_route_create_common ( vtl, name );
3673 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3677 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3679 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3680 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3682 if ( g_hash_table_size (vtl->routes) > 0 ) {
3683 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3684 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3685 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3686 vik_layers_panel_emit_update ( vlp );
3691 static void trw_layer_finish_track ( gpointer lav[2] )
3693 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3694 vtl->current_track = NULL;
3695 vik_layer_emit_update ( VIK_LAYER(vtl) );
3698 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3700 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3701 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3703 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3704 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3705 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3706 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3707 vik_layers_panel_emit_update ( vlp );
3711 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3713 /* NB do not care if wp is visible or not */
3714 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3717 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3719 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3720 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3722 /* Only 1 waypoint - jump straight to it */
3723 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3724 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3725 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3727 /* If at least 2 waypoints - find center and then zoom to fit */
3728 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3730 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3731 maxmin[0].lat = vtl->waypoints_bbox.north;
3732 maxmin[1].lat = vtl->waypoints_bbox.south;
3733 maxmin[0].lon = vtl->waypoints_bbox.east;
3734 maxmin[1].lon = vtl->waypoints_bbox.west;
3735 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3738 vik_layers_panel_emit_update ( vlp );
3741 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3743 static gpointer pass_along[2];
3745 GtkWidget *export_submenu;
3746 pass_along[0] = vtl;
3747 pass_along[1] = vlp;
3749 item = gtk_menu_item_new();
3750 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3751 gtk_widget_show ( item );
3753 if ( vtl->current_track ) {
3754 if ( vtl->current_track->is_route )
3755 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3757 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3759 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3760 gtk_widget_show ( item );
3763 item = gtk_menu_item_new ();
3764 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3765 gtk_widget_show ( item );
3768 /* Now with icons */
3769 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3770 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3771 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3772 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3773 gtk_widget_show ( item );
3775 GtkWidget *view_submenu = gtk_menu_new();
3776 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3777 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3778 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3779 gtk_widget_show ( item );
3780 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3782 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3784 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3785 gtk_widget_show ( item );
3787 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3788 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3789 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3790 gtk_widget_show ( item );
3792 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3794 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3795 gtk_widget_show ( item );
3797 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3798 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3800 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3801 gtk_widget_show ( item );
3803 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3805 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3806 gtk_widget_show ( item );
3808 export_submenu = gtk_menu_new ();
3809 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3810 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3811 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3812 gtk_widget_show ( item );
3813 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3815 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3816 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3817 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3818 gtk_widget_show ( item );
3820 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3821 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3822 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3823 gtk_widget_show ( item );
3825 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3826 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3827 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3828 gtk_widget_show ( item );
3830 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3831 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3832 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3833 gtk_widget_show ( item );
3835 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
3836 item = gtk_menu_item_new_with_mnemonic ( external1 );
3837 g_free ( external1 );
3838 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3839 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3840 gtk_widget_show ( item );
3842 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
3843 item = gtk_menu_item_new_with_mnemonic ( external2 );
3844 g_free ( external2 );
3845 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3846 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3847 gtk_widget_show ( item );
3849 GtkWidget *new_submenu = gtk_menu_new();
3850 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3851 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3852 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3853 gtk_widget_show(item);
3854 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3856 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3857 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3858 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3859 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3860 gtk_widget_show ( item );
3862 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3863 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3865 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3866 gtk_widget_show ( item );
3867 // Make it available only when a new track *not* already in progress
3868 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3870 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3871 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3873 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3874 gtk_widget_show ( item );
3875 // Make it available only when a new track *not* already in progress
3876 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3878 #ifdef VIK_CONFIG_GEOTAG
3879 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3880 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3881 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3882 gtk_widget_show ( item );
3885 GtkWidget *acquire_submenu = gtk_menu_new ();
3886 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3887 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3888 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3889 gtk_widget_show ( item );
3890 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3892 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3894 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3895 gtk_widget_show ( item );
3897 /* FIXME: only add menu when at least a routing engine has support for Directions */
3898 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3899 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3900 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3901 gtk_widget_show ( item );
3903 #ifdef VIK_CONFIG_OPENSTREETMAP
3904 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3905 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3906 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3907 gtk_widget_show ( item );
3909 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3911 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3912 gtk_widget_show ( item );
3915 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
3916 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
3917 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3918 gtk_widget_show ( item );
3920 #ifdef VIK_CONFIG_GEONAMES
3921 GtkWidget *wikipedia_submenu = gtk_menu_new();
3922 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3923 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3924 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3925 gtk_widget_show(item);
3926 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3928 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3929 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3930 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3931 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3932 gtk_widget_show ( item );
3934 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3935 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3937 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3938 gtk_widget_show ( item );
3941 #ifdef VIK_CONFIG_GEOCACHES
3942 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3943 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3944 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3945 gtk_widget_show ( item );
3948 #ifdef VIK_CONFIG_GEOTAG
3949 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3950 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3951 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3952 gtk_widget_show ( item );
3955 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3957 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3958 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
3959 gtk_widget_show ( item );
3961 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3963 GtkWidget *upload_submenu = gtk_menu_new ();
3964 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3965 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3966 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3967 gtk_widget_show ( item );
3968 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3970 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3971 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3972 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3973 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3974 gtk_widget_show ( item );
3976 #ifdef VIK_CONFIG_OPENSTREETMAP
3977 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3980 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3981 gtk_widget_show ( item );
3984 GtkWidget *delete_submenu = gtk_menu_new ();
3985 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3987 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3988 gtk_widget_show ( item );
3989 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3991 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3992 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3994 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3995 gtk_widget_show ( item );
3997 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3998 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4000 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4001 gtk_widget_show ( item );
4003 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4004 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4005 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4006 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4007 gtk_widget_show ( item );
4009 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4010 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4011 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4012 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4013 gtk_widget_show ( item );
4015 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4016 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4018 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4019 gtk_widget_show ( item );
4021 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4022 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4024 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4025 gtk_widget_show ( item );
4027 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4028 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4030 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4031 gtk_widget_show ( item );
4034 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4035 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4037 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4038 gtk_widget_show ( item );
4041 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4042 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4043 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4044 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4045 gtk_widget_show ( item );
4046 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4048 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4049 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4051 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4052 gtk_widget_show ( item );
4053 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4056 // Fake Waypoint UUIDs vi simple increasing integer
4057 static guint wp_uuid = 0;
4059 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4063 vik_waypoint_set_name (wp, name);
4065 if ( VIK_LAYER(vtl)->realized )
4067 // Do we need to create the sublayer:
4068 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4069 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4072 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4074 // Visibility column always needed for waypoints
4075 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 );
4077 // Actual setting of visibility dependent on the waypoint
4078 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4080 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4082 // Sort now as post_read is not called on a realized waypoint
4083 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4086 highest_wp_number_add_wp(vtl, name);
4087 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4091 // Fake Track UUIDs vi simple increasing integer
4092 static guint tr_uuid = 0;
4094 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4098 vik_track_set_name (t, name);
4100 if ( VIK_LAYER(vtl)->realized )
4102 // Do we need to create the sublayer:
4103 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4104 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4107 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4108 // Visibility column always needed for tracks
4109 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 );
4111 // Actual setting of visibility dependent on the track
4112 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4114 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4116 // Sort now as post_read is not called on a realized track
4117 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4120 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4122 trw_layer_update_treeview ( vtl, t );
4125 // Fake Route UUIDs vi simple increasing integer
4126 static guint rt_uuid = 0;
4128 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4132 vik_track_set_name (t, name);
4134 if ( VIK_LAYER(vtl)->realized )
4136 // Do we need to create the sublayer:
4137 if ( g_hash_table_size (vtl->routes) == 0 ) {
4138 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4141 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4142 // Visibility column always needed for routes
4143 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 );
4144 // Actual setting of visibility dependent on the route
4145 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4147 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4149 // Sort now as post_read is not called on a realized route
4150 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4153 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4155 trw_layer_update_treeview ( vtl, t );
4158 /* to be called whenever a track has been deleted or may have been changed. */
4159 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4161 if (vtl->current_tp_track == trk )
4162 trw_layer_cancel_current_tp ( vtl, FALSE );
4166 * Normally this is done to due the waypoint size preference having changed
4168 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4170 GHashTableIter iter;
4171 gpointer key, value;
4174 g_hash_table_iter_init ( &iter, vtl->waypoints );
4175 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4176 VikWaypoint *wp = VIK_WAYPOINT(value);
4178 // Reapply symbol setting to update the pixbuf
4179 gchar *tmp_symbol = g_strdup ( wp->symbol );
4180 vik_waypoint_set_symbol ( wp, tmp_symbol );
4181 g_free ( tmp_symbol );
4187 * trw_layer_new_unique_sublayer_name:
4189 * Allocates a unique new name
4191 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4194 gchar *newname = g_strdup(name);
4199 switch ( sublayer_type ) {
4200 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4201 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4203 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4204 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4207 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4210 // If found a name already in use try adding 1 to it and we try again
4212 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4214 newname = new_newname;
4217 } while ( id != NULL);
4222 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4224 // No more uniqueness of name forced when loading from a file
4225 // This now makes this function a little redunant as we just flow the parameters through
4226 vik_trw_layer_add_waypoint ( vtl, name, wp );
4229 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4231 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4232 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4233 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4234 vik_track_free ( tr );
4235 vtl->route_finder_append = FALSE; /* this means we have added it */
4238 // No more uniqueness of name forced when loading from a file
4240 vik_trw_layer_add_route ( vtl, name, tr );
4242 vik_trw_layer_add_track ( vtl, name, tr );
4244 if ( vtl->route_finder_check_added_track ) {
4245 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4246 vtl->route_finder_added_track = tr;
4251 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4253 *l = g_list_append(*l, id);
4257 * Move an item from one TRW layer to another TRW layer
4259 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4261 // TODO reconsider strategy when moving within layer (if anything...)
4262 gboolean rename = ( vtl_src != vtl_dest );
4266 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4267 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4271 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4273 newname = g_strdup ( trk->name );
4275 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4276 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4278 vik_trw_layer_delete_track ( vtl_src, trk );
4281 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4282 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4286 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4288 newname = g_strdup ( trk->name );
4290 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4291 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4293 vik_trw_layer_delete_route ( vtl_src, trk );
4296 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4297 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4301 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4303 newname = g_strdup ( wp->name );
4305 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4306 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4308 trw_layer_delete_waypoint ( vtl_src, wp );
4310 // Recalculate bounds even if not renamed as maybe dragged between layers
4311 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4312 trw_layer_calculate_bounds_waypoints ( vtl_src );
4316 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4318 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4319 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4321 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4322 GList *items = NULL;
4325 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4326 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4328 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4329 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4331 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4332 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4337 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4338 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4339 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4340 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4342 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4349 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4350 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4354 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4356 trku_udata *user_data = udata;
4357 if ( trk == user_data->trk ) {
4358 user_data->uuid = id;
4364 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4366 gboolean was_visible = FALSE;
4368 if ( trk && trk->name ) {
4370 if ( trk == vtl->current_track ) {
4371 vtl->current_track = NULL;
4372 vtl->current_tp_track = NULL;
4373 vtl->current_tp_id = NULL;
4374 vtl->moving_tp = FALSE;
4377 was_visible = trk->visible;
4379 if ( trk == vtl->route_finder_current_track )
4380 vtl->route_finder_current_track = NULL;
4382 if ( trk == vtl->route_finder_added_track )
4383 vtl->route_finder_added_track = NULL;
4389 // Hmmm, want key of it
4390 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4392 if ( trkf && udata.uuid ) {
4393 /* could be current_tp, so we have to check */
4394 trw_layer_cancel_tps_of_track ( vtl, trk );
4396 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4399 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4400 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4401 g_hash_table_remove ( vtl->tracks, udata.uuid );
4403 // If last sublayer, then remove sublayer container
4404 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4405 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4413 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4415 gboolean was_visible = FALSE;
4417 if ( trk && trk->name ) {
4419 if ( trk == vtl->current_track ) {
4420 vtl->current_track = NULL;
4421 vtl->current_tp_track = NULL;
4422 vtl->current_tp_id = NULL;
4423 vtl->moving_tp = FALSE;
4426 was_visible = trk->visible;
4428 if ( trk == vtl->route_finder_current_track )
4429 vtl->route_finder_current_track = NULL;
4431 if ( trk == vtl->route_finder_added_track )
4432 vtl->route_finder_added_track = NULL;
4438 // Hmmm, want key of it
4439 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4441 if ( trkf && udata.uuid ) {
4442 /* could be current_tp, so we have to check */
4443 trw_layer_cancel_tps_of_track ( vtl, trk );
4445 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4448 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4449 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4450 g_hash_table_remove ( vtl->routes, udata.uuid );
4452 // If last sublayer, then remove sublayer container
4453 if ( g_hash_table_size (vtl->routes) == 0 ) {
4454 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4462 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4464 gboolean was_visible = FALSE;
4466 if ( wp && wp->name ) {
4468 if ( wp == vtl->current_wp ) {
4469 vtl->current_wp = NULL;
4470 vtl->current_wp_id = NULL;
4471 vtl->moving_wp = FALSE;
4474 was_visible = wp->visible;
4480 // Hmmm, want key of it
4481 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4483 if ( wpf && udata.uuid ) {
4484 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4487 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4488 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4490 highest_wp_number_remove_wp(vtl, wp->name);
4491 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4493 // If last sublayer, then remove sublayer container
4494 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4495 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4505 // Only for temporary use by trw_layer_delete_waypoint_by_name
4506 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4508 wpu_udata *user_data = udata;
4509 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4510 user_data->uuid = id;
4517 * Delete a waypoint by the given name
4518 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4519 * as there be multiple waypoints with the same name
4521 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4524 // Fake a waypoint with the given name
4525 udata.wp = vik_waypoint_new ();
4526 vik_waypoint_set_name (udata.wp, name);
4527 // Currently only the name is used in this waypoint find function
4530 // Hmmm, want key of it
4531 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4533 vik_waypoint_free (udata.wp);
4535 if ( wpf && udata.uuid )
4536 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4542 VikTrack *trk; // input
4543 gpointer uuid; // output
4546 // Only for temporary use by trw_layer_delete_track_by_name
4547 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4549 tpu_udata *user_data = udata;
4550 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4551 user_data->uuid = id;
4558 * Delete a track by the given name
4559 * NOTE: ATM this will delete the first encountered Track with the specified name
4560 * as there may be multiple tracks with the same name within the specified hash table
4562 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4565 // Fake a track with the given name
4566 udata.trk = vik_track_new ();
4567 vik_track_set_name (udata.trk, name);
4568 // Currently only the name is used in this waypoint find function
4571 // Hmmm, want key of it
4572 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4574 vik_track_free (udata.trk);
4576 if ( trkf && udata.uuid ) {
4577 // This could be a little better written...
4578 if ( vtl->tracks == ht_tracks )
4579 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4580 if ( vtl->routes == ht_tracks )
4581 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4588 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4590 vik_treeview_item_delete (vt, it );
4593 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4596 vtl->current_track = NULL;
4597 vtl->route_finder_current_track = NULL;
4598 vtl->route_finder_added_track = NULL;
4599 if (vtl->current_tp_track)
4600 trw_layer_cancel_current_tp(vtl, FALSE);
4602 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4603 g_hash_table_remove_all(vtl->routes_iters);
4604 g_hash_table_remove_all(vtl->routes);
4606 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4608 vik_layer_emit_update ( VIK_LAYER(vtl) );
4611 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4614 vtl->current_track = NULL;
4615 vtl->route_finder_current_track = NULL;
4616 vtl->route_finder_added_track = NULL;
4617 if (vtl->current_tp_track)
4618 trw_layer_cancel_current_tp(vtl, FALSE);
4620 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4621 g_hash_table_remove_all(vtl->tracks_iters);
4622 g_hash_table_remove_all(vtl->tracks);
4624 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4626 vik_layer_emit_update ( VIK_LAYER(vtl) );
4629 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4631 vtl->current_wp = NULL;
4632 vtl->current_wp_id = NULL;
4633 vtl->moving_wp = FALSE;
4635 highest_wp_number_reset(vtl);
4637 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4638 g_hash_table_remove_all(vtl->waypoints_iters);
4639 g_hash_table_remove_all(vtl->waypoints);
4641 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4643 vik_layer_emit_update ( VIK_LAYER(vtl) );
4646 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4648 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4649 // Get confirmation from the user
4650 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4651 _("Are you sure you want to delete all tracks in %s?"),
4652 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4653 vik_trw_layer_delete_all_tracks (vtl);
4656 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4658 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4659 // Get confirmation from the user
4660 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4661 _("Are you sure you want to delete all routes in %s?"),
4662 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4663 vik_trw_layer_delete_all_routes (vtl);
4666 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4668 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4669 // Get confirmation from the user
4670 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4671 _("Are you sure you want to delete all waypoints in %s?"),
4672 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4673 vik_trw_layer_delete_all_waypoints (vtl);
4676 static void trw_layer_delete_item ( gpointer pass_along[6] )
4678 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4679 gboolean was_visible = FALSE;
4680 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4682 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4683 if ( wp && wp->name ) {
4684 if ( GPOINTER_TO_INT ( pass_along[4]) )
4685 // Get confirmation from the user
4686 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4687 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4688 _("Are you sure you want to delete the waypoint \"%s\"?"),
4691 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4694 else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4696 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4697 if ( trk && trk->name ) {
4698 if ( GPOINTER_TO_INT ( pass_along[4]) )
4699 // Get confirmation from the user
4700 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4701 _("Are you sure you want to delete the track \"%s\"?"),
4704 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4709 VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4710 if ( trk && trk->name ) {
4711 if ( GPOINTER_TO_INT ( pass_along[4]) )
4712 // Get confirmation from the user
4713 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4714 _("Are you sure you want to delete the route \"%s\"?"),
4717 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4721 vik_layer_emit_update ( VIK_LAYER(vtl) );
4725 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4727 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4729 vik_waypoint_set_name ( wp, new_name );
4731 // Now update the treeview as well
4736 // Need key of it for treeview update
4737 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4739 if ( wpf && udataU.uuid ) {
4740 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4743 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4744 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4750 * Maintain icon of waypoint in the treeview
4752 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4754 // update the treeview
4759 // Need key of it for treeview update
4760 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4762 if ( wpf && udataU.uuid ) {
4763 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4766 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4771 static void trw_layer_properties_item ( gpointer pass_along[7] )
4773 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4774 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4776 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4778 if ( wp && wp->name )
4780 gboolean updated = FALSE;
4781 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4783 trw_layer_waypoint_rename ( vtl, wp, new_name );
4785 if ( updated && pass_along[6] )
4786 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4788 if ( updated && VIK_LAYER(vtl)->visible )
4789 vik_layer_emit_update ( VIK_LAYER(vtl) );
4795 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4796 tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4798 tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4800 if ( tr && tr->name )
4802 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4805 pass_along[1], /* vlp */
4806 pass_along[5], /* vvp */
4813 * trw_layer_track_statistics:
4815 * Show track statistics.
4816 * ATM jump to the stats page in the properties
4817 * TODO: consider separating the stats into an individual dialog?
4819 static void trw_layer_track_statistics ( gpointer pass_along[7] )
4821 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4823 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4824 trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4826 trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4828 if ( trk && trk->name ) {
4829 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4832 pass_along[1], // vlp
4833 pass_along[5], // vvp
4839 * Update the treeview of the track id - primarily to update the icon
4841 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
4847 gpointer *trkf = NULL;
4848 if ( trk->is_route )
4849 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4851 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4853 if ( trkf && udata.uuid ) {
4855 GtkTreeIter *iter = NULL;
4856 if ( trk->is_route )
4857 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4859 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4862 // TODO: Make this a function
4863 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4864 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4865 ((trk->color.green & 0xff00) << 8) |
4866 (trk->color.blue & 0xff00);
4867 gdk_pixbuf_fill ( pixbuf, pixel );
4868 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4869 g_object_unref (pixbuf);
4876 Parameter 1 -> VikLayersPanel
4877 Parameter 2 -> VikLayer
4878 Parameter 3 -> VikViewport
4880 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4883 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4884 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4887 /* since vlp not set, vl & vvp should be valid instead! */
4889 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4890 vik_layer_emit_update ( VIK_LAYER(vl) );
4895 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4897 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4899 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4900 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4902 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4904 if ( track && track->trackpoints )
4905 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4908 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4910 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4912 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4913 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4915 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4917 if ( track && track->trackpoints )
4919 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4921 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4922 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4923 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4924 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4925 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4929 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4931 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4933 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4934 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4936 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4941 // Converting a track to a route can be a bit more complicated,
4942 // so give a chance to change our minds:
4943 if ( !trk->is_route &&
4944 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4945 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4947 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4948 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4953 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4956 trk_copy->is_route = !trk_copy->is_route;
4958 // ATM can't set name to self - so must create temporary copy
4959 gchar *name = g_strdup ( trk_copy->name );
4961 // Delete old one and then add new one
4962 if ( trk->is_route ) {
4963 vik_trw_layer_delete_route ( vtl, trk );
4964 vik_trw_layer_add_track ( vtl, name, trk_copy );
4967 // Extra route conversion bits...
4968 vik_track_merge_segments ( trk_copy );
4969 vik_track_to_routepoints ( trk_copy );
4971 vik_trw_layer_delete_track ( vtl, trk );
4972 vik_trw_layer_add_route ( vtl, name, trk_copy );
4976 // Update in case color of track / route changes when moving between sublayers
4977 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4980 static void trw_layer_anonymize_times ( gpointer pass_along[6] )
4982 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4984 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4985 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4987 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4990 vik_track_anonymize_times ( track );
4993 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4995 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4997 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4998 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5000 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5005 vtl->current_track = track;
5006 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);
5008 if ( track->trackpoints )
5009 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
5013 * extend a track using route finder
5015 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
5017 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5018 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
5021 if ( !track->trackpoints )
5023 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
5025 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5026 vtl->route_finder_coord = last_coord;
5027 vtl->route_finder_current_track = track;
5028 vtl->route_finder_started = TRUE;
5030 if ( track->trackpoints )
5031 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
5038 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5040 // If have a vlp then perform a basic test to see if any DEM info available...
5042 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5044 if ( !g_list_length(dems) ) {
5045 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5053 * apply_dem_data_common:
5055 * A common function for applying the DEM values and reporting the results.
5057 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5059 if ( !trw_layer_dem_test ( vtl, vlp ) )
5062 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5063 // Inform user how much was changed
5065 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5066 g_snprintf(str, 64, tmp_str, changed);
5067 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5070 static void trw_layer_apply_dem_data_all ( gpointer pass_along[6] )
5072 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5074 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5075 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5077 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5080 apply_dem_data_common ( vtl, pass_along[1], track, FALSE );
5083 static void trw_layer_apply_dem_data_only_missing ( gpointer pass_along[6] )
5085 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5087 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5088 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5090 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5093 apply_dem_data_common ( vtl, pass_along[1], track, TRUE );
5099 * A common function for applying the elevation smoothing and reporting the results.
5101 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5103 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5104 // Inform user how much was changed
5106 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5107 g_snprintf(str, 64, tmp_str, changed);
5108 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5114 static void trw_layer_missing_elevation_data_interp ( gpointer pass_along[6] )
5116 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5118 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5119 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5121 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5126 smooth_it ( vtl, track, FALSE );
5129 static void trw_layer_missing_elevation_data_flat ( gpointer pass_along[6] )
5131 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5133 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5134 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5136 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5141 smooth_it ( vtl, track, TRUE );
5145 * Commonal helper function
5147 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5150 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5151 g_snprintf(str, 64, tmp_str, changed);
5152 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5155 static void trw_layer_apply_dem_data_wpt_all ( gpointer pass_along[6] )
5157 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5158 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5160 if ( !trw_layer_dem_test ( vtl, vlp ) )
5164 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5166 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5168 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5172 GHashTableIter iter;
5173 gpointer key, value;
5175 g_hash_table_iter_init ( &iter, vtl->waypoints );
5176 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5177 VikWaypoint *wp = VIK_WAYPOINT(value);
5178 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5181 wp_changed_message ( vtl, changed );
5184 static void trw_layer_apply_dem_data_wpt_only_missing ( gpointer pass_along[6] )
5186 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5187 VikLayersPanel *vlp = (VikLayersPanel *)pass_along[1];
5189 if ( !trw_layer_dem_test ( vtl, vlp ) )
5193 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5195 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
5197 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5201 GHashTableIter iter;
5202 gpointer key, value;
5204 g_hash_table_iter_init ( &iter, vtl->waypoints );
5205 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5206 VikWaypoint *wp = VIK_WAYPOINT(value);
5207 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5210 wp_changed_message ( vtl, changed );
5213 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
5215 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5217 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5218 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5220 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5225 GList *trps = track->trackpoints;
5228 trps = g_list_last(trps);
5229 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
5232 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
5234 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5236 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5237 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5239 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5244 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5247 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5250 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
5252 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5254 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5255 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5257 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5262 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5265 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5268 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
5270 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5272 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5273 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5275 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5280 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5283 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
5287 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5289 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
5291 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5293 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5294 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5296 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5298 if ( trk && trk->trackpoints )
5300 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5301 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5302 trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
5303 if ( pass_along[1] )
5304 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
5306 vik_layer_emit_update ( VIK_LAYER(vtl) );
5311 * Refine the selected track/route with a routing engine.
5312 * The routing engine is selected by the user, when requestiong the job.
5314 static void trw_layer_route_refine ( gpointer pass_along[6] )
5316 static gint last_engine = 0;
5317 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5320 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5321 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5323 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5325 if ( trk && trk->trackpoints )
5327 /* Check size of the route */
5328 int nb = vik_track_get_tp_count(trk);
5330 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5331 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5332 GTK_MESSAGE_WARNING,
5333 GTK_BUTTONS_OK_CANCEL,
5334 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5336 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5337 gtk_widget_destroy ( dialog );
5338 if (response != GTK_RESPONSE_OK )
5341 /* Select engine from dialog */
5342 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5343 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5344 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5346 GTK_RESPONSE_REJECT,
5348 GTK_RESPONSE_ACCEPT,
5350 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5351 gtk_widget_show_all(label);
5353 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5355 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5356 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5357 gtk_widget_show_all(combo);
5359 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5361 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5363 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5365 /* Dialog validated: retrieve selected engine and do the job */
5366 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5367 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5370 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5372 /* Force saving track */
5373 /* FIXME: remove or rename this hack */
5374 vtl->route_finder_check_added_track = TRUE;
5377 vik_routing_engine_refine (routing, vtl, trk);
5379 /* FIXME: remove or rename this hack */
5380 if ( vtl->route_finder_added_track )
5381 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5383 vtl->route_finder_added_track = NULL;
5384 vtl->route_finder_check_added_track = FALSE;
5386 vik_layer_emit_update ( VIK_LAYER(vtl) );
5388 /* Restore cursor */
5389 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0])) );
5391 gtk_widget_destroy ( dialog );
5395 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
5397 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5398 trw_layer_tpwin_init ( vtl );
5401 /*************************************
5402 * merge/split by time routines
5403 *************************************/
5405 /* called for each key in track hash table.
5406 * If the current track has the same time stamp type, add it to the result,
5407 * except the one pointed by "exclude".
5408 * set exclude to NULL if there is no exclude to check.
5409 * Note that the result is in reverse (for performance reasons).
5414 gboolean with_timestamps;
5416 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5418 twt_udata *user_data = udata;
5419 VikTrackpoint *p1, *p2;
5421 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
5425 if (VIK_TRACK(value)->trackpoints) {
5426 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
5427 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
5429 if ( user_data->with_timestamps ) {
5430 if (!p1->has_timestamp || !p2->has_timestamp) {
5435 // Don't add tracks with timestamps when getting non timestamp tracks
5436 if (p1->has_timestamp || p2->has_timestamp) {
5442 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5445 /* called for each key in track hash table. if original track user_data[1] is close enough
5446 * to the passed one, add it to list in user_data[0]
5448 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5451 VikTrackpoint *p1, *p2;
5452 VikTrack *trk = VIK_TRACK(value);
5454 GList **nearby_tracks = ((gpointer *)user_data)[0];
5455 GList *tpoints = ((gpointer *)user_data)[1];
5458 * detect reasons for not merging, and return
5459 * if no reason is found not to merge, then do it.
5462 // Exclude the original track from the compiled list
5463 if (trk->trackpoints == tpoints) {
5467 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
5468 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
5470 if (trk->trackpoints) {
5471 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
5472 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
5474 if (!p1->has_timestamp || !p2->has_timestamp) {
5475 //g_print("no timestamp\n");
5479 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5480 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5481 if (! (abs(t1 - p2->timestamp) < threshold ||
5483 abs(p1->timestamp - t2) < threshold)
5490 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5493 /* comparison function used to sort tracks; a and b are hash table keys */
5494 /* Not actively used - can be restored if needed
5495 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5497 GHashTable *tracks = user_data;
5500 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5501 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5503 if (t1 < t2) return -1;
5504 if (t1 > t2) return 1;
5509 /* comparison function used to sort trackpoints */
5510 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5512 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5514 if (t1 < t2) return -1;
5515 if (t1 > t2) return 1;
5520 * comparison function which can be used to sort tracks or waypoints by name
5522 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5524 const gchar* namea = (const gchar*) a;
5525 const gchar* nameb = (const gchar*) b;
5526 if ( namea == NULL || nameb == NULL)
5529 // Same sort method as used in the vik_treeview_*_alphabetize functions
5530 return strcmp ( namea, nameb );
5534 * Attempt to merge selected track with other tracks specified by the user
5535 * Tracks to merge with must be of the same 'type' as the selected track -
5536 * either all with timestamps, or all without timestamps
5538 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
5540 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5541 GList *other_tracks = NULL;
5542 GHashTable *ght_tracks;
5543 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5544 ght_tracks = vtl->routes;
5546 ght_tracks = vtl->tracks;
5548 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5553 if ( !track->trackpoints )
5557 udata.result = &other_tracks;
5558 udata.exclude = track->trackpoints;
5559 // Allow merging with 'similar' time type time tracks
5560 // i.e. either those times, or those without
5561 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
5563 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5564 other_tracks = g_list_reverse(other_tracks);
5566 if ( !other_tracks ) {
5567 if ( udata.with_timestamps )
5568 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5570 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5574 // Sort alphabetically for user presentation
5575 // Convert into list of names for usage with dialog function
5576 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5577 GList *other_tracks_names = NULL;
5578 GList *iter = g_list_first ( other_tracks );
5580 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5581 iter = g_list_next ( iter );
5584 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5586 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5590 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5591 g_list_free(other_tracks);
5592 g_list_free(other_tracks_names);
5597 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5598 VikTrack *merge_track;
5599 if ( track->is_route )
5600 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5602 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5605 vik_track_steal_and_append_trackpoints ( track, merge_track );
5606 if ( track->is_route )
5607 vik_trw_layer_delete_route (vtl, merge_track);
5609 vik_trw_layer_delete_track (vtl, merge_track);
5610 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5613 for (l = merge_list; l != NULL; l = g_list_next(l))
5615 g_list_free(merge_list);
5617 vik_layer_emit_update( VIK_LAYER(vtl) );
5621 // c.f. trw_layer_sorted_track_id_by_name_list
5622 // but don't add the specified track to the list (normally current track)
5623 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5625 twt_udata *user_data = udata;
5628 if (trk->trackpoints == user_data->exclude) {
5632 // Sort named list alphabetically
5633 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5637 * Join - this allows combining 'tracks' and 'track routes'
5638 * i.e. doesn't care about whether tracks have consistent timestamps
5639 * ATM can only append one track at a time to the currently selected track
5641 static void trw_layer_append_track ( gpointer pass_along[6] )
5644 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5646 GHashTable *ght_tracks;
5647 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5648 ght_tracks = vtl->routes;
5650 ght_tracks = vtl->tracks;
5652 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
5657 GList *other_tracks_names = NULL;
5659 // Sort alphabetically for user presentation
5660 // Convert into list of names for usage with dialog function
5661 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5663 udata.result = &other_tracks_names;
5664 udata.exclude = trk->trackpoints;
5666 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5668 // Note the limit to selecting one track only
5669 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5670 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5671 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5674 trk->is_route ? _("Append Route"): _("Append Track"),
5675 trk->is_route ? _("Select the route to append after the current route") :
5676 _("Select the track to append after the current track") );
5678 g_list_free(other_tracks_names);
5680 // It's a list, but shouldn't contain more than one other track!
5681 if ( append_list ) {
5683 for (l = append_list; l != NULL; l = g_list_next(l)) {
5684 // TODO: at present this uses the first track found by name,
5685 // which with potential multiple same named tracks may not be the one selected...
5686 VikTrack *append_track;
5687 if ( trk->is_route )
5688 append_track = vik_trw_layer_get_route ( vtl, l->data );
5690 append_track = vik_trw_layer_get_track ( vtl, l->data );
5692 if ( append_track ) {
5693 vik_track_steal_and_append_trackpoints ( trk, append_track );
5694 if ( trk->is_route )
5695 vik_trw_layer_delete_route (vtl, append_track);
5697 vik_trw_layer_delete_track (vtl, append_track);
5700 for (l = append_list; l != NULL; l = g_list_next(l))
5702 g_list_free(append_list);
5704 vik_layer_emit_update( VIK_LAYER(vtl) );
5709 * Very similar to trw_layer_append_track for joining
5710 * but this allows selection from the 'other' list
5711 * If a track is selected, then is shows routes and joins the selected one
5712 * If a route is selected, then is shows tracks and joins the selected one
5714 static void trw_layer_append_other ( gpointer pass_along[6] )
5717 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5719 GHashTable *ght_mykind, *ght_others;
5720 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5721 ght_mykind = vtl->routes;
5722 ght_others = vtl->tracks;
5725 ght_mykind = vtl->tracks;
5726 ght_others = vtl->routes;
5729 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5734 GList *other_tracks_names = NULL;
5736 // Sort alphabetically for user presentation
5737 // Convert into list of names for usage with dialog function
5738 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5740 udata.result = &other_tracks_names;
5741 udata.exclude = trk->trackpoints;
5743 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5745 // Note the limit to selecting one track only
5746 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5747 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5748 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5751 trk->is_route ? _("Append Track"): _("Append Route"),
5752 trk->is_route ? _("Select the track to append after the current route") :
5753 _("Select the route to append after the current track") );
5755 g_list_free(other_tracks_names);
5757 // It's a list, but shouldn't contain more than one other track!
5758 if ( append_list ) {
5760 for (l = append_list; l != NULL; l = g_list_next(l)) {
5761 // TODO: at present this uses the first track found by name,
5762 // which with potential multiple same named tracks may not be the one selected...
5764 // Get FROM THE OTHER TYPE list
5765 VikTrack *append_track;
5766 if ( trk->is_route )
5767 append_track = vik_trw_layer_get_track ( vtl, l->data );
5769 append_track = vik_trw_layer_get_route ( vtl, l->data );
5771 if ( append_track ) {
5773 if ( !append_track->is_route &&
5774 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5775 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5777 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5778 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5779 vik_track_merge_segments ( append_track );
5780 vik_track_to_routepoints ( append_track );
5787 vik_track_steal_and_append_trackpoints ( trk, append_track );
5789 // Delete copied which is FROM THE OTHER TYPE list
5790 if ( trk->is_route )
5791 vik_trw_layer_delete_track (vtl, append_track);
5793 vik_trw_layer_delete_route (vtl, append_track);
5796 for (l = append_list; l != NULL; l = g_list_next(l))
5798 g_list_free(append_list);
5799 vik_layer_emit_update( VIK_LAYER(vtl) );
5803 /* merge by segments */
5804 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5806 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5807 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5808 guint segments = vik_track_merge_segments ( trk );
5809 // NB currently no need to redraw as segments not actually shown on the display
5810 // However inform the user of what happened:
5812 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5813 g_snprintf(str, 64, tmp_str, segments);
5814 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5817 /* merge by time routine */
5818 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5820 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5824 GList *tracks_with_timestamp = NULL;
5825 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5826 if (orig_trk->trackpoints &&
5827 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5828 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5833 udata.result = &tracks_with_timestamp;
5834 udata.exclude = orig_trk->trackpoints;
5835 udata.with_timestamps = TRUE;
5836 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5837 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5839 if (!tracks_with_timestamp) {
5840 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5843 g_list_free(tracks_with_timestamp);
5845 static guint threshold_in_minutes = 1;
5846 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5847 _("Merge Threshold..."),
5848 _("Merge when time between tracks less than:"),
5849 &threshold_in_minutes)) {
5853 // keep attempting to merge all tracks until no merges within the time specified is possible
5854 gboolean attempt_merge = TRUE;
5855 GList *nearby_tracks = NULL;
5857 static gpointer params[3];
5859 while ( attempt_merge ) {
5861 // Don't try again unless tracks have changed
5862 attempt_merge = FALSE;
5864 trps = orig_trk->trackpoints;
5868 if (nearby_tracks) {
5869 g_list_free(nearby_tracks);
5870 nearby_tracks = NULL;
5873 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5874 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5876 /* g_print("Original track times: %d and %d\n", t1, t2); */
5877 params[0] = &nearby_tracks;
5878 params[1] = (gpointer)trps;
5879 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5881 /* get a list of adjacent-in-time tracks */
5882 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5885 GList *l = nearby_tracks;
5888 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5889 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5891 t1 = get_first_trackpoint(l)->timestamp;
5892 t2 = get_last_trackpoint(l)->timestamp;
5893 #undef get_first_trackpoint
5894 #undef get_last_trackpoint
5895 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5898 /* remove trackpoints from merged track, delete track */
5899 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5900 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5902 // Tracks have changed, therefore retry again against all the remaining tracks
5903 attempt_merge = TRUE;
5908 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5911 g_list_free(nearby_tracks);
5913 vik_layer_emit_update( VIK_LAYER(vtl) );
5917 * Split a track at the currently selected trackpoint
5919 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5921 if ( !vtl->current_tpl )
5924 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5925 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5927 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5928 GList *newglist = g_list_alloc ();
5929 newglist->prev = NULL;
5930 newglist->next = vtl->current_tpl->next;
5931 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5932 tr->trackpoints = newglist;
5934 vtl->current_tpl->next->prev = newglist; /* end old track here */
5935 vtl->current_tpl->next = NULL;
5937 // Bounds of the selected track changed due to the split
5938 vik_track_calculate_bounds ( vtl->current_tp_track );
5940 vtl->current_tpl = newglist; /* change tp to first of new track. */
5941 vtl->current_tp_track = tr;
5944 vik_trw_layer_add_route ( vtl, name, tr );
5946 vik_trw_layer_add_track ( vtl, name, tr );
5948 // Bounds of the new track created by the split
5949 vik_track_calculate_bounds ( tr );
5955 // Also need id of newly created track
5958 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5960 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5962 if ( trkf && udata.uuid )
5963 vtl->current_tp_id = udata.uuid;
5965 vtl->current_tp_id = NULL;
5967 vik_layer_emit_update(VIK_LAYER(vtl));
5973 /* split by time routine */
5974 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5976 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5977 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5978 GList *trps = track->trackpoints;
5980 GList *newlists = NULL;
5981 GList *newtps = NULL;
5982 static guint thr = 1;
5989 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5990 _("Split Threshold..."),
5991 _("Split when time between trackpoints exceeds:"),
5996 /* iterate through trackpoints, and copy them into new lists without touching original list */
5997 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6001 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6003 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6006 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6007 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6008 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6010 goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
6015 if (ts - prev_ts > thr*60) {
6016 /* flush accumulated trackpoints into new list */
6017 newlists = g_list_append(newlists, g_list_reverse(newtps));
6021 /* accumulate trackpoint copies in newtps, in reverse order */
6022 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6024 iter = g_list_next(iter);
6027 newlists = g_list_append(newlists, g_list_reverse(newtps));
6030 /* put lists of trackpoints into tracks */
6032 // Only bother updating if the split results in new tracks
6033 if (g_list_length (newlists) > 1) {
6038 tr = vik_track_copy ( track, FALSE );
6039 tr->trackpoints = (GList *)(iter->data);
6041 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6042 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6043 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
6044 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
6045 g_free ( new_tr_name );
6046 vik_track_calculate_bounds ( tr );
6047 iter = g_list_next(iter);
6049 // Remove original track and then update the display
6050 vik_trw_layer_delete_track (vtl, track);
6051 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6053 g_list_free(newlists);
6057 * Split a track by the number of points as specified by the user
6059 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
6061 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6063 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6064 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6066 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6071 // Check valid track
6072 GList *trps = track->trackpoints;
6076 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
6077 _("Split Every Nth Point"),
6078 _("Split on every Nth point:"),
6079 250, // Default value as per typical limited track capacity of various GPS devices
6083 // Was a valid number returned?
6089 GList *newlists = NULL;
6090 GList *newtps = NULL;
6095 /* accumulate trackpoint copies in newtps, in reverse order */
6096 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6098 if (count >= points) {
6099 /* flush accumulated trackpoints into new list */
6100 newlists = g_list_append(newlists, g_list_reverse(newtps));
6104 iter = g_list_next(iter);
6107 // If there is a remaining chunk put that into the new split list
6108 // This may well be the whole track if no split points were encountered
6110 newlists = g_list_append(newlists, g_list_reverse(newtps));
6113 /* put lists of trackpoints into tracks */
6115 // Only bother updating if the split results in new tracks
6116 if (g_list_length (newlists) > 1) {
6121 tr = vik_track_copy ( track, FALSE );
6122 tr->trackpoints = (GList *)(iter->data);
6124 if ( track->is_route ) {
6125 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6126 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6129 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6130 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6132 g_free ( new_tr_name );
6133 vik_track_calculate_bounds ( tr );
6135 iter = g_list_next(iter);
6137 // Remove original track and then update the display
6138 if ( track->is_route )
6139 vik_trw_layer_delete_route (vtl, track);
6141 vik_trw_layer_delete_track (vtl, track);
6142 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
6144 g_list_free(newlists);
6148 * Split a track at the currently selected trackpoint
6150 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
6152 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6153 gint subtype = GPOINTER_TO_INT (pass_along[2]);
6154 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6158 * Split a track by its segments
6159 * Routes do not have segments so don't call this for routes
6161 static void trw_layer_split_segments ( gpointer pass_along[6] )
6163 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6164 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6171 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6174 for ( i = 0; i < ntracks; i++ ) {
6176 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6177 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6178 g_free ( new_tr_name );
6183 // Remove original track
6184 vik_trw_layer_delete_track ( vtl, trk );
6185 vik_layer_emit_update ( VIK_LAYER(vtl) );
6188 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6191 /* end of split/merge routines */
6193 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6197 // Find available adjacent trackpoint
6198 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6199 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6200 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6202 // Delete current trackpoint
6203 vik_trackpoint_free ( vtl->current_tpl->data );
6204 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6206 // Set to current to the available adjacent trackpoint
6207 vtl->current_tpl = new_tpl;
6209 if ( vtl->current_tp_track ) {
6210 vik_track_calculate_bounds ( vtl->current_tp_track );
6214 // Delete current trackpoint
6215 vik_trackpoint_free ( vtl->current_tpl->data );
6216 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6217 trw_layer_cancel_current_tp ( vtl, FALSE );
6222 * Delete the selected point
6224 static void trw_layer_delete_point_selected ( gpointer pass_along[6] )
6226 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6228 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6229 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6231 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6236 if ( !vtl->current_tpl )
6239 trw_layer_trackpoint_selected_delete ( vtl, trk );
6241 // Track has been updated so update tps:
6242 trw_layer_cancel_tps_of_track ( vtl, trk );
6244 vik_layer_emit_update ( VIK_LAYER(vtl) );
6248 * Delete adjacent track points at the same position
6249 * AKA Delete Dulplicates on the Properties Window
6251 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
6253 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6255 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6256 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6258 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6263 gulong removed = vik_track_remove_dup_points ( trk );
6265 // Track has been updated so update tps:
6266 trw_layer_cancel_tps_of_track ( vtl, trk );
6268 // Inform user how much was deleted as it's not obvious from the normal view
6270 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6271 g_snprintf(str, 64, tmp_str, removed);
6272 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6274 vik_layer_emit_update ( VIK_LAYER(vtl) );
6278 * Delete adjacent track points with the same timestamp
6279 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6281 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
6283 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6285 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6286 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6288 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6293 gulong removed = vik_track_remove_same_time_points ( trk );
6295 // Track has been updated so update tps:
6296 trw_layer_cancel_tps_of_track ( vtl, trk );
6298 // Inform user how much was deleted as it's not obvious from the normal view
6300 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6301 g_snprintf(str, 64, tmp_str, removed);
6302 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6304 vik_layer_emit_update ( VIK_LAYER(vtl) );
6310 static void trw_layer_insert_point_after ( gpointer pass_along[6] )
6312 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6314 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6315 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6317 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6322 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6324 vik_layer_emit_update ( VIK_LAYER(vtl) );
6327 static void trw_layer_insert_point_before ( gpointer pass_along[6] )
6329 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6331 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6332 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6334 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6339 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6341 vik_layer_emit_update ( VIK_LAYER(vtl) );
6347 static void trw_layer_reverse ( gpointer pass_along[6] )
6349 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
6351 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6352 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
6354 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
6359 vik_track_reverse ( track );
6361 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
6365 * Similar to trw_layer_enum_item, but this uses a sorted method
6368 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6370 GList **list = (GList**)udata;
6371 // *list = g_list_prepend(*all, key); //unsorted method
6372 // Sort named list alphabetically
6373 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6378 * Now Waypoint specific sort
6380 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6382 GList **list = (GList**)udata;
6383 // Sort named list alphabetically
6384 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6388 * Track specific sort
6390 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6392 GList **list = (GList**)udata;
6393 // Sort named list alphabetically
6394 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6399 gboolean has_same_track_name;
6400 const gchar *same_track_name;
6401 } same_track_name_udata;
6403 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6405 const gchar* namea = (const gchar*) aa;
6406 const gchar* nameb = (const gchar*) bb;
6409 gint result = strcmp ( namea, nameb );
6411 if ( result == 0 ) {
6412 // Found two names the same
6413 same_track_name_udata *user_data = udata;
6414 user_data->has_same_track_name = TRUE;
6415 user_data->same_track_name = namea;
6418 // Leave ordering the same
6423 * Find out if any tracks have the same name in this hash table
6425 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6427 // Sort items by name, then compare if any next to each other are the same
6429 GList *track_names = NULL;
6430 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6433 if ( ! track_names )
6436 same_track_name_udata udata;
6437 udata.has_same_track_name = FALSE;
6439 // Use sort routine to traverse list comparing items
6440 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6441 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6442 // Still no tracks...
6446 return udata.has_same_track_name;
6450 * Force unqiue track names for the track table specified
6451 * Note the panel is a required parameter to enable the update of the names displayed
6452 * Specify if on tracks or else on routes
6454 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6456 // . Search list for an instance of repeated name
6457 // . get track of this name
6458 // . create new name
6459 // . rename track & update equiv. treeview iter
6460 // . repeat until all different
6462 same_track_name_udata udata;
6464 GList *track_names = NULL;
6465 udata.has_same_track_name = FALSE;
6466 udata.same_track_name = NULL;
6468 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6471 if ( ! track_names )
6474 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6476 // Still no tracks...
6477 if ( ! dummy_list1 )
6480 while ( udata.has_same_track_name ) {
6482 // Find a track with the same name
6485 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6487 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6491 g_critical("Houston, we've had a problem.");
6492 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6493 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6498 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6499 vik_track_set_name ( trk, newname );
6505 // Need want key of it for treeview update
6506 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6508 if ( trkf && udataU.uuid ) {
6512 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6514 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6517 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6519 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6521 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6525 // Start trying to find same names again...
6527 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6528 udata.has_same_track_name = FALSE;
6529 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6531 // No tracks any more - give up searching
6532 if ( ! dummy_list2 )
6533 udata.has_same_track_name = FALSE;
6537 vik_layers_panel_emit_update ( vlp );
6540 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
6542 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6545 switch (GPOINTER_TO_INT (pass_along[2])) {
6546 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6547 iter = &(vtl->tracks_iter);
6548 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6550 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6551 iter = &(vtl->routes_iter);
6552 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6554 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6555 iter = &(vtl->waypoints_iter);
6556 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6560 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6563 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
6565 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
6568 switch (GPOINTER_TO_INT (pass_along[2])) {
6569 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6570 iter = &(vtl->tracks_iter);
6571 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6573 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6574 iter = &(vtl->routes_iter);
6575 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6577 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6578 iter = &(vtl->waypoints_iter);
6579 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6583 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6589 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
6591 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6594 // Ensure list of track names offered is unique
6595 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6596 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6597 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6598 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
6604 // Sort list alphabetically for better presentation
6605 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6608 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6612 // Get list of items to delete from the user
6613 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6616 _("Delete Selection"),
6617 _("Select tracks to delete"));
6620 // Delete requested tracks
6621 // since specificly requested, IMHO no need for extra confirmation
6622 if ( delete_list ) {
6624 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6625 // This deletes first trk it finds of that name (but uniqueness is enforced above)
6626 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6628 g_list_free(delete_list);
6629 vik_layer_emit_update( VIK_LAYER(vtl) );
6636 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
6638 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6641 // Ensure list of track names offered is unique
6642 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6643 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6644 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6645 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
6651 // Sort list alphabetically for better presentation
6652 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6655 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6659 // Get list of items to delete from the user
6660 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6663 _("Delete Selection"),
6664 _("Select routes to delete") );
6667 // Delete requested routes
6668 // since specificly requested, IMHO no need for extra confirmation
6669 if ( delete_list ) {
6671 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6672 // This deletes first route it finds of that name (but uniqueness is enforced above)
6673 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6675 g_list_free(delete_list);
6676 vik_layer_emit_update( VIK_LAYER(vtl) );
6681 gboolean has_same_waypoint_name;
6682 const gchar *same_waypoint_name;
6683 } same_waypoint_name_udata;
6685 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6687 const gchar* namea = (const gchar*) aa;
6688 const gchar* nameb = (const gchar*) bb;
6691 gint result = strcmp ( namea, nameb );
6693 if ( result == 0 ) {
6694 // Found two names the same
6695 same_waypoint_name_udata *user_data = udata;
6696 user_data->has_same_waypoint_name = TRUE;
6697 user_data->same_waypoint_name = namea;
6700 // Leave ordering the same
6705 * Find out if any waypoints have the same name in this layer
6707 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6709 // Sort items by name, then compare if any next to each other are the same
6711 GList *waypoint_names = NULL;
6712 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6715 if ( ! waypoint_names )
6718 same_waypoint_name_udata udata;
6719 udata.has_same_waypoint_name = FALSE;
6721 // Use sort routine to traverse list comparing items
6722 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6723 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6724 // Still no waypoints...
6728 return udata.has_same_waypoint_name;
6732 * Force unqiue waypoint names for this layer
6733 * Note the panel is a required parameter to enable the update of the names displayed
6735 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6737 // . Search list for an instance of repeated name
6738 // . get waypoint of this name
6739 // . create new name
6740 // . rename waypoint & update equiv. treeview iter
6741 // . repeat until all different
6743 same_waypoint_name_udata udata;
6745 GList *waypoint_names = NULL;
6746 udata.has_same_waypoint_name = FALSE;
6747 udata.same_waypoint_name = NULL;
6749 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6752 if ( ! waypoint_names )
6755 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6757 // Still no waypoints...
6758 if ( ! dummy_list1 )
6761 while ( udata.has_same_waypoint_name ) {
6763 // Find a waypoint with the same name
6764 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6768 g_critical("Houston, we've had a problem.");
6769 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6770 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6775 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
6777 trw_layer_waypoint_rename ( vtl, waypoint, newname );
6779 // Start trying to find same names again...
6780 waypoint_names = NULL;
6781 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6782 udata.has_same_waypoint_name = FALSE;
6783 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6785 // No waypoints any more - give up searching
6786 if ( ! dummy_list2 )
6787 udata.has_same_waypoint_name = FALSE;
6791 vik_layers_panel_emit_update ( vlp );
6797 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6799 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6802 // Ensure list of waypoint names offered is unique
6803 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6804 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6805 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6806 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6812 // Sort list alphabetically for better presentation
6813 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6815 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6819 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6821 // Get list of items to delete from the user
6822 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6825 _("Delete Selection"),
6826 _("Select waypoints to delete"));
6829 // Delete requested waypoints
6830 // since specificly requested, IMHO no need for extra confirmation
6831 if ( delete_list ) {
6833 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6834 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6835 trw_layer_delete_waypoint_by_name (vtl, l->data);
6837 g_list_free(delete_list);
6839 trw_layer_calculate_bounds_waypoints ( vtl );
6840 vik_layer_emit_update( VIK_LAYER(vtl) );
6848 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6850 vik_treeview_item_toggle_visible ( vt, it );
6856 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6858 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6864 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6866 wp->visible = GPOINTER_TO_INT (on_off);
6872 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6874 wp->visible = !wp->visible;
6880 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6882 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6883 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6884 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6885 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6887 vik_layer_emit_update ( VIK_LAYER(vtl) );
6893 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6895 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6896 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6897 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6898 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6900 vik_layer_emit_update ( VIK_LAYER(vtl) );
6906 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6908 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6909 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6910 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6912 vik_layer_emit_update ( VIK_LAYER(vtl) );
6918 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6920 trk->visible = GPOINTER_TO_INT (on_off);
6926 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6928 trk->visible = !trk->visible;
6934 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6936 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6937 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6938 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6939 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6941 vik_layer_emit_update ( VIK_LAYER(vtl) );
6947 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6949 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6950 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6951 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6952 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6954 vik_layer_emit_update ( VIK_LAYER(vtl) );
6960 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6962 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6963 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6964 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6966 vik_layer_emit_update ( VIK_LAYER(vtl) );
6972 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6974 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6975 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6976 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6977 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6979 vik_layer_emit_update ( VIK_LAYER(vtl) );
6985 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6987 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6988 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6989 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6990 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6992 vik_layer_emit_update ( VIK_LAYER(vtl) );
6998 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
7000 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7001 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7002 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7004 vik_layer_emit_update ( VIK_LAYER(vtl) );
7008 * vik_trw_layer_build_waypoint_list_t:
7010 * Helper function to construct a list of #vik_trw_waypoint_list_t
7012 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7014 GList *waypoints_and_layers = NULL;
7015 // build waypoints_and_layers list
7016 while ( waypoints ) {
7017 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7018 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7020 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7021 waypoints = g_list_next ( waypoints );
7023 return waypoints_and_layers;
7027 * trw_layer_create_waypoint_list:
7029 * Create the latest list of waypoints with the associated layer(s)
7030 * Although this will always be from a single layer here
7032 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7034 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7035 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7037 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7041 * trw_layer_analyse_close:
7043 * Stuff to do on dialog closure
7045 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7047 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7048 gtk_widget_destroy ( dialog );
7049 vtl->tracks_analysis_dialog = NULL;
7053 * vik_trw_layer_build_track_list_t:
7055 * Helper function to construct a list of #vik_trw_track_list_t
7057 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7059 GList *tracks_and_layers = NULL;
7060 // build tracks_and_layers list
7062 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7063 vtdl->trk = VIK_TRACK(tracks->data);
7065 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7066 tracks = g_list_next ( tracks );
7068 return tracks_and_layers;
7072 * trw_layer_create_track_list:
7074 * Create the latest list of tracks with the associated layer(s)
7075 * Although this will always be from a single layer here
7077 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7079 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7080 GList *tracks = NULL;
7081 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7082 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7084 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7086 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7089 static void trw_layer_tracks_stats ( gpointer lav[2] )
7091 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7092 // There can only be one!
7093 if ( vtl->tracks_analysis_dialog )
7096 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7097 VIK_LAYER(vtl)->name,
7099 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7100 trw_layer_create_track_list,
7101 trw_layer_analyse_close );
7107 static void trw_layer_routes_stats ( gpointer lav[2] )
7109 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
7110 // There can only be one!
7111 if ( vtl->tracks_analysis_dialog )
7114 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7115 VIK_LAYER(vtl)->name,
7117 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7118 trw_layer_create_track_list,
7119 trw_layer_analyse_close );
7122 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
7124 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7126 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
7129 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
7131 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7134 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7135 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7139 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
7141 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
7144 if ( !strncmp(wp->comment, "http", 4) ) {
7145 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
7146 } else if ( !strncmp(wp->description, "http", 4) ) {
7147 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
7151 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7153 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7155 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7157 // No actual change to the name supplied
7159 if (strcmp(newname, wp->name) == 0 )
7162 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7165 // An existing waypoint has been found with the requested name
7166 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7167 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7172 // Update WP name and refresh the treeview
7173 vik_waypoint_set_name (wp, newname);
7175 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7176 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7178 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7183 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7185 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7187 // No actual change to the name supplied
7189 if (strcmp(newname, trk->name) == 0)
7192 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7195 // An existing track has been found with the requested name
7196 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7197 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7201 // Update track name and refresh GUI parts
7202 vik_track_set_name (trk, newname);
7204 // Update any subwindows that could be displaying this track which has changed name
7205 // Only one Track Edit Window
7206 if ( l->current_tp_track == trk && l->tpwin ) {
7207 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7209 // Property Dialog of the track
7210 vik_trw_layer_propwin_update ( trk );
7212 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7213 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7215 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7220 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7222 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7224 // No actual change to the name supplied
7226 if (strcmp(newname, trk->name) == 0)
7229 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7232 // An existing track has been found with the requested name
7233 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7234 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7238 // Update track name and refresh GUI parts
7239 vik_track_set_name (trk, newname);
7241 // Update any subwindows that could be displaying this track which has changed name
7242 // Only one Track Edit Window
7243 if ( l->current_tp_track == trk && l->tpwin ) {
7244 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7246 // Property Dialog of the track
7247 vik_trw_layer_propwin_update ( trk );
7249 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7250 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7252 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7259 static gboolean is_valid_geocache_name ( gchar *str )
7261 gint len = strlen ( str );
7262 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]));
7265 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
7267 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7268 a_acquire_set_filter_track ( trk );
7271 #ifdef VIK_CONFIG_GOOGLE
7272 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7274 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7275 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7278 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
7280 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
7282 gchar *escaped = uri_escape ( tr->comment );
7283 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7284 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
7291 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7292 /* viewpoint is now available instead */
7293 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7295 static gpointer pass_along[8];
7297 gboolean rv = FALSE;
7300 pass_along[1] = vlp;
7301 pass_along[2] = GINT_TO_POINTER (subtype);
7302 pass_along[3] = sublayer;
7303 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
7304 pass_along[5] = vvp;
7305 pass_along[6] = iter;
7306 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
7308 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7312 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7314 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7315 gtk_widget_show ( item );
7317 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7318 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7319 if (tr && tr->property_dialog)
7320 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7322 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7323 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7324 if (tr && tr->property_dialog)
7325 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7328 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7329 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7330 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7331 gtk_widget_show ( item );
7333 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7336 gtk_widget_show ( item );
7338 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7340 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7341 gtk_widget_show ( item );
7343 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7345 // Always create separator as now there is always at least the transform menu option
7346 item = gtk_menu_item_new ();
7347 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7348 gtk_widget_show ( item );
7350 /* could be a right-click using the tool */
7351 if ( vlp != NULL ) {
7352 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7353 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7354 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7355 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7356 gtk_widget_show ( item );
7359 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7361 if ( wp && wp->name ) {
7362 if ( is_valid_geocache_name ( wp->name ) ) {
7363 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7365 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7366 gtk_widget_show ( item );
7368 #ifdef VIK_CONFIG_GEOTAG
7369 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7370 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7371 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7372 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7373 gtk_widget_show ( item );
7377 if ( wp && wp->image )
7379 // Set up image paramater
7380 pass_along[5] = wp->image;
7382 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7383 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
7384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7385 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7386 gtk_widget_show ( item );
7388 #ifdef VIK_CONFIG_GEOTAG
7389 GtkWidget *geotag_submenu = gtk_menu_new ();
7390 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7391 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7392 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7393 gtk_widget_show ( item );
7394 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7396 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7397 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7398 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7399 gtk_widget_show ( item );
7401 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7402 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7403 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7404 gtk_widget_show ( item );
7410 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7411 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7412 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7413 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7414 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7415 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7416 gtk_widget_show ( item );
7422 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7423 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7424 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7425 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7426 gtk_widget_show ( item );
7427 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7428 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7429 gtk_widget_set_sensitive ( item, TRUE );
7431 gtk_widget_set_sensitive ( item, FALSE );
7434 item = gtk_menu_item_new ();
7435 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7436 gtk_widget_show ( item );
7439 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7442 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7443 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7444 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7445 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7446 gtk_widget_show ( item );
7449 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7451 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7452 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7453 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7454 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7455 gtk_widget_show ( item );
7457 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7458 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7459 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7460 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7461 gtk_widget_show ( item );
7463 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7464 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7466 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7467 gtk_widget_show ( item );
7469 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7470 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7471 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7472 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7473 gtk_widget_show ( item );
7475 GtkWidget *vis_submenu = gtk_menu_new ();
7476 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7477 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7478 gtk_widget_show ( item );
7479 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7481 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7482 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7483 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7484 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7485 gtk_widget_show ( item );
7487 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7488 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7489 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7490 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7491 gtk_widget_show ( item );
7493 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7494 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7495 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7496 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7497 gtk_widget_show ( item );
7499 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7500 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7501 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7502 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7505 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7509 if ( l->current_track && !l->current_track->is_route ) {
7510 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7511 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7512 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7513 gtk_widget_show ( item );
7515 item = gtk_menu_item_new ();
7516 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7517 gtk_widget_show ( item );
7520 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7521 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7522 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7523 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7524 gtk_widget_show ( item );
7526 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7527 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7529 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7530 gtk_widget_show ( item );
7531 // Make it available only when a new track *not* already in progress
7532 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7534 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7535 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7537 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7538 gtk_widget_show ( item );
7540 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7541 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7542 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7543 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7544 gtk_widget_show ( item );
7546 GtkWidget *vis_submenu = gtk_menu_new ();
7547 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7548 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7549 gtk_widget_show ( item );
7550 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7552 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7553 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7554 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7555 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7556 gtk_widget_show ( item );
7558 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7559 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7560 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7561 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7562 gtk_widget_show ( item );
7564 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7565 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7566 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7567 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7569 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7570 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7571 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7572 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7573 gtk_widget_show ( item );
7575 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7577 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7578 gtk_widget_show ( item );
7581 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7585 if ( l->current_track && l->current_track->is_route ) {
7586 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7587 // Reuse finish track method
7588 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7589 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7590 gtk_widget_show ( item );
7592 item = gtk_menu_item_new ();
7593 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7594 gtk_widget_show ( item );
7597 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7598 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7600 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7601 gtk_widget_show ( item );
7603 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7604 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7605 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7606 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7607 gtk_widget_show ( item );
7608 // Make it available only when a new track *not* already in progress
7609 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7611 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7612 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7613 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7614 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7615 gtk_widget_show ( item );
7617 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7618 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7619 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7620 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7621 gtk_widget_show ( item );
7623 GtkWidget *vis_submenu = gtk_menu_new ();
7624 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7625 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7626 gtk_widget_show ( item );
7627 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7629 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7630 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7631 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7632 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7633 gtk_widget_show ( item );
7635 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7636 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7637 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7638 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7639 gtk_widget_show ( item );
7641 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7642 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7643 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7644 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7646 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7647 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7648 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7649 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7651 gtk_widget_show ( item );
7653 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7654 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7655 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7656 gtk_widget_show ( item );
7660 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7661 GtkWidget *submenu_sort = gtk_menu_new ();
7662 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7663 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7664 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7665 gtk_widget_show ( item );
7666 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7668 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7669 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7670 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7671 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7672 gtk_widget_show ( item );
7674 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7675 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7676 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7677 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7678 gtk_widget_show ( item );
7681 GtkWidget *upload_submenu = gtk_menu_new ();
7683 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7685 item = gtk_menu_item_new ();
7686 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7687 gtk_widget_show ( item );
7689 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7690 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7691 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7692 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7693 if ( l->current_track ) {
7694 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7695 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7696 gtk_widget_show ( item );
7699 item = gtk_menu_item_new ();
7700 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7701 gtk_widget_show ( item );
7704 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7705 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7707 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7708 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7710 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7711 gtk_widget_show ( item );
7713 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7714 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7715 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7716 gtk_widget_show ( item );
7718 GtkWidget *goto_submenu;
7719 goto_submenu = gtk_menu_new ();
7720 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7721 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7722 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7723 gtk_widget_show ( item );
7724 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7726 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7727 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7728 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7729 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7730 gtk_widget_show ( item );
7732 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7733 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7734 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7735 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7736 gtk_widget_show ( item );
7738 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7741 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7742 gtk_widget_show ( item );
7744 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7745 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7746 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7747 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7748 gtk_widget_show ( item );
7750 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7751 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7753 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7754 gtk_widget_show ( item );
7756 // Routes don't have speeds
7757 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7758 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7760 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7761 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7762 gtk_widget_show ( item );
7765 GtkWidget *combine_submenu;
7766 combine_submenu = gtk_menu_new ();
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7769 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7770 gtk_widget_show ( item );
7771 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
7773 // Routes don't have times or segments...
7774 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7775 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7776 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7777 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7778 gtk_widget_show ( item );
7780 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7781 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7782 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7783 gtk_widget_show ( item );
7786 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
7787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
7788 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7789 gtk_widget_show ( item );
7791 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7792 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7794 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
7795 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7796 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7797 gtk_widget_show ( item );
7799 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7800 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7802 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7803 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7804 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7805 gtk_widget_show ( item );
7807 GtkWidget *split_submenu;
7808 split_submenu = gtk_menu_new ();
7809 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7810 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7811 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7812 gtk_widget_show ( item );
7813 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
7815 // Routes don't have times or segments...
7816 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7817 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7820 gtk_widget_show ( item );
7822 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7823 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7825 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7826 gtk_widget_show ( item );
7829 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7831 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7832 gtk_widget_show ( item );
7834 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7836 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7837 gtk_widget_show ( item );
7838 // Make it available only when a trackpoint is selected.
7839 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7841 GtkWidget *insert_submenu = gtk_menu_new ();
7842 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7843 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7844 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7845 gtk_widget_show ( item );
7846 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7848 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7849 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7850 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7851 gtk_widget_show ( item );
7852 // Make it available only when a point is selected
7853 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7855 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7856 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7857 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7858 gtk_widget_show ( item );
7859 // Make it available only when a point is selected
7860 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7862 GtkWidget *delete_submenu;
7863 delete_submenu = gtk_menu_new ();
7864 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7865 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7866 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7867 gtk_widget_show ( item );
7868 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7870 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7871 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7872 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7873 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7874 gtk_widget_show ( item );
7875 // Make it available only when a point is selected
7876 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7878 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7879 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7880 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7881 gtk_widget_show ( item );
7883 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7884 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7885 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7886 gtk_widget_show ( item );
7888 GtkWidget *transform_submenu;
7889 transform_submenu = gtk_menu_new ();
7890 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7891 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7892 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7893 gtk_widget_show ( item );
7894 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7896 GtkWidget *dem_submenu;
7897 dem_submenu = gtk_menu_new ();
7898 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7899 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
7900 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7901 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7903 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7905 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7906 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7907 gtk_widget_show ( item );
7909 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
7910 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
7911 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7912 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
7913 gtk_widget_show ( item );
7915 GtkWidget *smooth_submenu;
7916 smooth_submenu = gtk_menu_new ();
7917 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
7918 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7919 gtk_widget_show ( item );
7920 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
7922 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
7923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
7924 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7925 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
7926 gtk_widget_show ( item );
7928 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
7929 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
7930 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
7931 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
7932 gtk_widget_show ( item );
7934 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7935 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7937 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7938 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7940 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7941 gtk_widget_show ( item );
7943 // Routes don't have timestamps - so this is only available for tracks
7944 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7945 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
7946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
7948 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
7949 gtk_widget_show ( item );
7952 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7953 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7955 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7956 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7958 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7959 gtk_widget_show ( item );
7961 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7962 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
7963 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
7964 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
7965 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7966 gtk_widget_show ( item );
7969 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7971 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7972 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7974 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7975 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
7976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7977 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7978 gtk_widget_show ( item );
7981 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7982 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7984 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7985 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7986 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7987 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7988 gtk_widget_show ( item );
7990 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7991 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7993 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7994 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7996 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7997 gtk_widget_show ( item );
7999 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8000 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8001 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
8002 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8003 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8004 gtk_widget_show ( item );
8007 // ATM can't upload a single waypoint but can do waypoints to a GPS
8008 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8009 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8010 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8011 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8012 gtk_widget_show ( item );
8013 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8015 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8016 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8018 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8019 gtk_widget_show ( item );
8023 #ifdef VIK_CONFIG_GOOGLE
8024 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8026 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8029 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8030 gtk_widget_show ( item );
8034 // Some things aren't usable with routes
8035 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8036 #ifdef VIK_CONFIG_OPENSTREETMAP
8037 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8038 // Convert internal pointer into actual track for usage outside this file
8039 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
8040 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
8042 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8043 gtk_widget_show ( item );
8046 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8047 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8049 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8050 gtk_widget_show ( item );
8052 /* ATM This function is only available via the layers panel, due to needing a vlp */
8054 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8055 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8056 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8058 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8059 gtk_widget_show ( item );
8063 #ifdef VIK_CONFIG_GEOTAG
8064 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8065 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8066 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8067 gtk_widget_show ( item );
8071 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8072 // Only show on viewport popmenu when a trackpoint is selected
8073 if ( ! vlp && l->current_tpl ) {
8075 item = gtk_menu_item_new ();
8076 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8077 gtk_widget_show ( item );
8079 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8080 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8082 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8083 gtk_widget_show ( item );
8087 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8088 GtkWidget *transform_submenu;
8089 transform_submenu = gtk_menu_new ();
8090 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8091 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8092 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8093 gtk_widget_show ( item );
8094 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8096 GtkWidget *dem_submenu;
8097 dem_submenu = gtk_menu_new ();
8098 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8099 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
8100 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8101 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8105 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8106 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8107 gtk_widget_show ( item );
8109 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8110 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8111 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8112 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8113 gtk_widget_show ( item );
8116 gtk_widget_show_all ( GTK_WIDGET(menu) );
8121 // TODO: Probably better to rework this track manipulation in viktrack.c
8122 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8125 if (!vtl->current_tpl)
8128 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8129 VikTrackpoint *tp_other = NULL;
8132 if (!vtl->current_tpl->prev)
8134 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8136 if (!vtl->current_tpl->next)
8138 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8141 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8144 VikTrackpoint *tp_new = vik_trackpoint_new();
8145 struct LatLon ll_current, ll_other;
8146 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8147 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8149 /* main positional interpolation */
8150 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8151 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8153 /* Now other properties that can be interpolated */
8154 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8156 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8157 /* Note here the division is applied to each part, then added
8158 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8159 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8160 tp_new->has_timestamp = TRUE;
8163 if (tp_current->speed != NAN && tp_other->speed != NAN)
8164 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8166 /* TODO - improve interpolation of course, as it may not be correct.
8167 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8168 [similar applies if value is in radians] */
8169 if (tp_current->course != NAN && tp_other->course != NAN)
8170 tp_new->course = (tp_current->course + tp_other->course)/2;
8172 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8174 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8175 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8177 // Otherwise try routes
8178 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8182 gint index = g_list_index ( trk->trackpoints, tp_current );
8186 // NB no recalculation of bounds since it is inserted between points
8187 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8192 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8198 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8202 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8204 if ( vtl->current_tpl )
8206 vtl->current_tpl = NULL;
8207 vtl->current_tp_track = NULL;
8208 vtl->current_tp_id = NULL;
8209 vik_layer_emit_update(VIK_LAYER(vtl));
8213 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8215 g_assert ( vtl->tpwin != NULL );
8216 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8217 trw_layer_cancel_current_tp ( vtl, TRUE );
8219 if ( vtl->current_tpl == NULL )
8222 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8224 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8225 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8227 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8229 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8231 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8235 trw_layer_trackpoint_selected_delete ( vtl, tr );
8237 if ( vtl->current_tpl )
8238 // Reset dialog with the available adjacent trackpoint
8239 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8241 vik_layer_emit_update(VIK_LAYER(vtl));
8243 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8245 if ( vtl->current_tp_track )
8246 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8247 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8249 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8251 if ( vtl->current_tp_track )
8252 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8253 vik_layer_emit_update(VIK_LAYER(vtl));
8255 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8257 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8258 vik_layer_emit_update(VIK_LAYER(vtl));
8260 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8261 vik_layer_emit_update(VIK_LAYER(vtl));
8265 * trw_layer_dialog_shift:
8266 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8268 * Try to reposition a dialog if it's over the specified coord
8269 * so to not obscure the item of interest
8271 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8273 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8275 // Attempt force dialog to be shown so we can find out where it is more reliably...
8276 while ( gtk_events_pending() )
8277 gtk_main_iteration ();
8279 // get parent window position & size
8280 gint win_pos_x, win_pos_y;
8281 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8283 gint win_size_x, win_size_y;
8284 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8286 // get own dialog size
8287 gint dia_size_x, dia_size_y;
8288 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8290 // get own dialog position
8291 gint dia_pos_x, dia_pos_y;
8292 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8294 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8295 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8297 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8299 gint vp_xx, vp_yy; // In viewport pixels
8300 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8302 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8306 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8308 // Transform Viewport pixels into absolute pixels
8309 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8310 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8312 // Is dialog over the point (to within an ^^ edge value)
8313 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8314 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8318 gint hh = vik_viewport_get_height ( vvp );
8320 // Consider the difference in viewport to the full window
8321 gint offset_y = dest_y;
8322 // Add difference between dialog and window sizes
8323 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8325 if ( vp_yy > hh/2 ) {
8326 // Point in bottom half, move window to top half
8327 gtk_window_move ( dialog, dia_pos_x, offset_y );
8330 // Point in top half, move dialog down
8331 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8335 // Shift left<->right
8336 gint ww = vik_viewport_get_width ( vvp );
8338 // Consider the difference in viewport to the full window
8339 gint offset_x = dest_x;
8340 // Add difference between dialog and window sizes
8341 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8343 if ( vp_xx > ww/2 ) {
8344 // Point on right, move window to left
8345 gtk_window_move ( dialog, offset_x, dia_pos_y );
8348 // Point on left, move right
8349 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8357 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8361 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8362 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8363 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8364 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8366 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8368 if ( vtl->current_tpl ) {
8369 // get tp pixel position
8370 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8372 // Shift up<->down to try not to obscure the trackpoint.
8373 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8377 if ( vtl->current_tpl )
8378 if ( vtl->current_tp_track )
8379 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8380 /* set layer name and TP data */
8383 /***************************************************************************
8385 ***************************************************************************/
8387 /*** Utility data structures and functions ****/
8391 gint closest_x, closest_y;
8392 gboolean draw_images;
8393 gpointer *closest_wp_id;
8394 VikWaypoint *closest_wp;
8400 gint closest_x, closest_y;
8401 gpointer closest_track_id;
8402 VikTrackpoint *closest_tp;
8408 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8414 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8416 // If waypoint has an image then use the image size to select
8417 if ( params->draw_images && wp->image ) {
8418 gint slackx, slacky;
8419 slackx = wp->image_width / 2;
8420 slacky = wp->image_height / 2;
8422 if ( x <= params->x + slackx && x >= params->x - slackx
8423 && y <= params->y + slacky && y >= params->y - slacky ) {
8424 params->closest_wp_id = id;
8425 params->closest_wp = wp;
8426 params->closest_x = x;
8427 params->closest_y = y;
8430 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8431 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8432 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8434 params->closest_wp_id = id;
8435 params->closest_wp = wp;
8436 params->closest_x = x;
8437 params->closest_y = y;
8441 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8443 GList *tpl = t->trackpoints;
8449 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8455 tp = VIK_TRACKPOINT(tpl->data);
8457 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8459 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8460 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8461 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8463 params->closest_track_id = id;
8464 params->closest_tp = tp;
8465 params->closest_tpl = tpl;
8466 params->closest_x = x;
8467 params->closest_y = y;
8473 // ATM: Leave this as 'Track' only.
8474 // Not overly bothered about having a snap to route trackpoint capability
8475 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8477 TPSearchParams params;
8481 params.closest_track_id = NULL;
8482 params.closest_tp = NULL;
8483 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8484 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
8485 return params.closest_tp;
8488 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8490 WPSearchParams params;
8494 params.draw_images = vtl->drawimages;
8495 params.closest_wp = NULL;
8496 params.closest_wp_id = NULL;
8497 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8498 return params.closest_wp;
8502 // Some forward declarations
8503 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8504 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8505 static void marker_end_move ( tool_ed_t *t );
8508 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8512 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8514 // Here always allow snapping back to the original location
8515 // this is useful when one decides not to move the thing afterall
8516 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8519 if ( event->state & GDK_CONTROL_MASK )
8521 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8523 new_coord = tp->coord;
8527 if ( event->state & GDK_SHIFT_MASK )
8529 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8531 new_coord = wp->coord;
8535 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8537 marker_moveto ( t, x, y );
8544 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8546 if ( t->holding && event->button == 1 )
8549 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8552 if ( event->state & GDK_CONTROL_MASK )
8554 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8556 new_coord = tp->coord;
8560 if ( event->state & GDK_SHIFT_MASK )
8562 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8564 new_coord = wp->coord;
8567 marker_end_move ( t );
8569 // Determine if working on a waypoint or a trackpoint
8570 if ( t->is_waypoint ) {
8571 // Update waypoint position
8572 vtl->current_wp->coord = new_coord;
8573 trw_layer_calculate_bounds_waypoints ( vtl );
8574 // Reset waypoint pointer
8575 vtl->current_wp = NULL;
8576 vtl->current_wp_id = NULL;
8579 if ( vtl->current_tpl ) {
8580 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8582 if ( vtl->current_tp_track )
8583 vik_track_calculate_bounds ( vtl->current_tp_track );
8586 if ( vtl->current_tp_track )
8587 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8588 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8592 vik_layer_emit_update ( VIK_LAYER(vtl) );
8599 Returns true if a waypoint or track is found near the requested event position for this particular layer
8600 The item found is automatically selected
8601 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8603 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8605 if ( event->button != 1 )
8608 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8611 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8615 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8617 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8619 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8620 WPSearchParams wp_params;
8621 wp_params.vvp = vvp;
8622 wp_params.x = event->x;
8623 wp_params.y = event->y;
8624 wp_params.draw_images = vtl->drawimages;
8625 wp_params.closest_wp_id = NULL;
8626 wp_params.closest_wp = NULL;
8628 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8630 if ( wp_params.closest_wp ) {
8633 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8635 // Too easy to move it so must be holding shift to start immediately moving it
8636 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8637 if ( event->state & GDK_SHIFT_MASK ||
8638 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8639 // Put into 'move buffer'
8640 // NB vvp & vw already set in tet
8641 tet->vtl = (gpointer)vtl;
8642 tet->is_waypoint = TRUE;
8644 marker_begin_move (tet, event->x, event->y);
8647 vtl->current_wp = wp_params.closest_wp;
8648 vtl->current_wp_id = wp_params.closest_wp_id;
8650 vik_layer_emit_update ( VIK_LAYER(vtl) );
8656 // Used for both track and route lists
8657 TPSearchParams tp_params;
8658 tp_params.vvp = vvp;
8659 tp_params.x = event->x;
8660 tp_params.y = event->y;
8661 tp_params.closest_track_id = NULL;
8662 tp_params.closest_tp = NULL;
8663 tp_params.closest_tpl = NULL;
8664 tp_params.bbox = bbox;
8666 if (vtl->tracks_visible) {
8667 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8669 if ( tp_params.closest_tp ) {
8671 // Always select + highlight the track
8672 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8674 tet->is_waypoint = FALSE;
8676 // Select the Trackpoint
8677 // Can move it immediately when control held or it's the previously selected tp
8678 if ( event->state & GDK_CONTROL_MASK ||
8679 vtl->current_tpl == tp_params.closest_tpl ) {
8680 // Put into 'move buffer'
8681 // NB vvp & vw already set in tet
8682 tet->vtl = (gpointer)vtl;
8683 marker_begin_move (tet, event->x, event->y);
8686 vtl->current_tpl = tp_params.closest_tpl;
8687 vtl->current_tp_id = tp_params.closest_track_id;
8688 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8690 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8693 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8695 vik_layer_emit_update ( VIK_LAYER(vtl) );
8700 // Try again for routes
8701 if (vtl->routes_visible) {
8702 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8704 if ( tp_params.closest_tp ) {
8706 // Always select + highlight the track
8707 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8709 tet->is_waypoint = FALSE;
8711 // Select the Trackpoint
8712 // Can move it immediately when control held or it's the previously selected tp
8713 if ( event->state & GDK_CONTROL_MASK ||
8714 vtl->current_tpl == tp_params.closest_tpl ) {
8715 // Put into 'move buffer'
8716 // NB vvp & vw already set in tet
8717 tet->vtl = (gpointer)vtl;
8718 marker_begin_move (tet, event->x, event->y);
8721 vtl->current_tpl = tp_params.closest_tpl;
8722 vtl->current_tp_id = tp_params.closest_track_id;
8723 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8725 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8728 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8730 vik_layer_emit_update ( VIK_LAYER(vtl) );
8735 /* these aren't the droids you're looking for */
8736 vtl->current_wp = NULL;
8737 vtl->current_wp_id = NULL;
8738 trw_layer_cancel_current_tp ( vtl, FALSE );
8741 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8746 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8748 if ( event->button != 3 )
8751 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8754 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8757 /* Post menu for the currently selected item */
8759 /* See if a track is selected */
8760 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8761 if ( track && track->visible ) {
8763 if ( track->name ) {
8765 if ( vtl->track_right_click_menu )
8766 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
8768 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8775 if ( track->is_route )
8776 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8778 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8780 if ( trkf && udataU.uuid ) {
8783 if ( track->is_route )
8784 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8786 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
8788 trw_layer_sublayer_add_menu_items ( vtl,
8789 vtl->track_right_click_menu,
8791 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
8797 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8803 /* See if a waypoint is selected */
8804 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8805 if ( waypoint && waypoint->visible ) {
8806 if ( waypoint->name ) {
8808 if ( vtl->wp_right_click_menu )
8809 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8811 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8814 udata.wp = waypoint;
8817 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8819 if ( wpf && udata.uuid ) {
8820 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8822 trw_layer_sublayer_add_menu_items ( vtl,
8823 vtl->wp_right_click_menu,
8825 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8830 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8839 /* background drawing hook, to be passed the viewport */
8840 static gboolean tool_sync_done = TRUE;
8842 static gboolean tool_sync(gpointer data)
8844 VikViewport *vvp = data;
8845 gdk_threads_enter();
8846 vik_viewport_sync(vvp);
8847 tool_sync_done = TRUE;
8848 gdk_threads_leave();
8852 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8855 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8856 gdk_gc_set_function ( t->gc, GDK_INVERT );
8857 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8858 vik_viewport_sync(t->vvp);
8863 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8865 VikViewport *vvp = t->vvp;
8866 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8867 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8871 if (tool_sync_done) {
8872 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8873 tool_sync_done = FALSE;
8877 static void marker_end_move ( tool_ed_t *t )
8879 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8880 g_object_unref ( t->gc );
8884 /*** Edit waypoint ****/
8886 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8888 tool_ed_t *t = g_new(tool_ed_t, 1);
8894 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8899 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8901 WPSearchParams params;
8902 tool_ed_t *t = data;
8903 VikViewport *vvp = t->vvp;
8905 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8912 if ( !vtl->vl.visible || !vtl->waypoints_visible )
8915 if ( vtl->current_wp && vtl->current_wp->visible )
8917 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
8919 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
8921 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
8922 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
8924 if ( event->button == 3 )
8925 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8927 marker_begin_move(t, event->x, event->y);
8934 params.x = event->x;
8935 params.y = event->y;
8936 params.draw_images = vtl->drawimages;
8937 params.closest_wp_id = NULL;
8938 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8939 params.closest_wp = NULL;
8940 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
8941 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8943 // how do we get here?
8944 marker_begin_move(t, event->x, event->y);
8945 g_critical("shouldn't be here");
8948 else if ( params.closest_wp )
8950 if ( event->button == 3 )
8951 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8953 vtl->waypoint_rightclick = FALSE;
8955 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8957 vtl->current_wp = params.closest_wp;
8958 vtl->current_wp_id = params.closest_wp_id;
8960 /* could make it so don't update if old WP is off screen and new is null but oh well */
8961 vik_layer_emit_update ( VIK_LAYER(vtl) );
8965 vtl->current_wp = NULL;
8966 vtl->current_wp_id = NULL;
8967 vtl->waypoint_rightclick = FALSE;
8968 vik_layer_emit_update ( VIK_LAYER(vtl) );
8972 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8974 tool_ed_t *t = data;
8975 VikViewport *vvp = t->vvp;
8977 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8982 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8985 if ( event->state & GDK_CONTROL_MASK )
8987 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8989 new_coord = tp->coord;
8993 if ( event->state & GDK_SHIFT_MASK )
8995 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8996 if ( wp && wp != vtl->current_wp )
8997 new_coord = wp->coord;
9002 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9004 marker_moveto ( t, x, y );
9011 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9013 tool_ed_t *t = data;
9014 VikViewport *vvp = t->vvp;
9016 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9019 if ( t->holding && event->button == 1 )
9022 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9025 if ( event->state & GDK_CONTROL_MASK )
9027 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9029 new_coord = tp->coord;
9033 if ( event->state & GDK_SHIFT_MASK )
9035 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9036 if ( wp && wp != vtl->current_wp )
9037 new_coord = wp->coord;
9040 marker_end_move ( t );
9042 vtl->current_wp->coord = new_coord;
9044 trw_layer_calculate_bounds_waypoints ( vtl );
9045 vik_layer_emit_update ( VIK_LAYER(vtl) );
9048 /* PUT IN RIGHT PLACE!!! */
9049 if ( event->button == 3 && vtl->waypoint_rightclick )
9051 if ( vtl->wp_right_click_menu )
9052 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9053 if ( vtl->current_wp ) {
9054 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9055 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 );
9056 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9058 vtl->waypoint_rightclick = FALSE;
9063 /*** New track ****/
9065 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9072 GdkDrawable *drawable;
9078 * Draw specified pixmap
9080 static gboolean draw_sync ( gpointer data )
9082 draw_sync_t *ds = (draw_sync_t*) data;
9083 // Sometimes don't want to draw
9084 // normally because another update has taken precedent such as panning the display
9085 // which means this pixmap is no longer valid
9086 if ( ds->vtl->draw_sync_do ) {
9087 gdk_threads_enter();
9088 gdk_draw_drawable (ds->drawable,
9091 0, 0, 0, 0, -1, -1);
9092 ds->vtl->draw_sync_done = TRUE;
9093 gdk_threads_leave();
9099 static gchar* distance_string (gdouble distance)
9103 /* draw label with distance */
9104 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9105 switch (dist_units) {
9106 case VIK_UNITS_DISTANCE_MILES:
9107 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9108 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9109 } else if (distance < 1609.4) {
9110 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9112 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9116 // VIK_UNITS_DISTANCE_KILOMETRES
9117 if (distance >= 1000 && distance < 100000) {
9118 g_sprintf(str, "%3.2f km", distance/1000.0);
9119 } else if (distance < 1000) {
9120 g_sprintf(str, "%d m", (int)distance);
9122 g_sprintf(str, "%d km", (int)distance/1000);
9126 return g_strdup (str);
9130 * Actually set the message in statusbar
9132 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9134 // Only show elevation data when track has some elevation properties
9135 gchar str_gain_loss[64];
9136 str_gain_loss[0] = '\0';
9137 gchar str_last_step[64];
9138 str_last_step[0] = '\0';
9139 gchar *str_total = distance_string (distance);
9141 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9142 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9143 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9145 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9148 if ( last_step > 0 ) {
9149 gchar *tmp = distance_string (last_step);
9150 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9154 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9156 // Write with full gain/loss information
9157 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9158 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9160 g_free ( str_total );
9164 * Figure out what information should be set in the statusbar and then write it
9166 static void update_statusbar ( VikTrwLayer *vtl )
9168 // Get elevation data
9169 gdouble elev_gain, elev_loss;
9170 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9172 /* Find out actual distance of current track */
9173 gdouble distance = vik_track_get_length (vtl->current_track);
9175 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9179 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9181 /* if we haven't sync'ed yet, we don't have time to do more. */
9182 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9183 GList *iter = g_list_last ( vtl->current_track->trackpoints );
9184 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
9186 static GdkPixmap *pixmap = NULL;
9188 // Need to check in case window has been resized
9189 w1 = vik_viewport_get_width(vvp);
9190 h1 = vik_viewport_get_height(vvp);
9192 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9194 gdk_drawable_get_size (pixmap, &w2, &h2);
9195 if (w1 != w2 || h1 != h2) {
9196 g_object_unref ( G_OBJECT ( pixmap ) );
9197 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9200 // Reset to background
9201 gdk_draw_drawable (pixmap,
9202 vtl->current_track_newpoint_gc,
9203 vik_viewport_get_pixmap(vvp),
9204 0, 0, 0, 0, -1, -1);
9206 draw_sync_t *passalong;
9209 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9211 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9212 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9213 // thus when we come to reset to the background it would include what we have already drawn!!
9214 gdk_draw_line ( pixmap,
9215 vtl->current_track_newpoint_gc,
9216 x1, y1, event->x, event->y );
9217 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9219 /* Find out actual distance of current track */
9220 gdouble distance = vik_track_get_length (vtl->current_track);
9222 // Now add distance to where the pointer is //
9225 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9226 vik_coord_to_latlon ( &coord, &ll );
9227 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9228 distance = distance + last_step;
9230 // Get elevation data
9231 gdouble elev_gain, elev_loss;
9232 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9234 // Adjust elevation data (if available) for the current pointer position
9236 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9237 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9238 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9239 // Adjust elevation of last track point
9240 if ( elev_new > last_tpt->altitude )
9242 elev_gain += elev_new - last_tpt->altitude;
9245 elev_loss += last_tpt->altitude - elev_new;
9250 // Display of the distance 'tooltip' during track creation is controlled by a preference
9252 if ( a_vik_get_create_track_tooltip() ) {
9254 gchar *str = distance_string (distance);
9256 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9257 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9258 pango_layout_set_text (pl, str, -1);
9260 pango_layout_get_pixel_size ( pl, &wd, &hd );
9263 // offset from cursor a bit depending on font size
9267 // Create a background block to make the text easier to read over the background map
9268 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9269 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9270 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9272 g_object_unref ( G_OBJECT ( pl ) );
9273 g_object_unref ( G_OBJECT ( background_block_gc ) );
9277 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9278 passalong->vtl = vtl;
9279 passalong->pixmap = pixmap;
9280 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9281 passalong->gc = vtl->current_track_newpoint_gc;
9285 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9287 // Update statusbar with full gain/loss information
9288 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9290 // draw pixmap when we have time to
9291 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9292 vtl->draw_sync_done = FALSE;
9293 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9295 return VIK_LAYER_TOOL_ACK;
9298 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9300 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9301 vtl->current_track = NULL;
9302 vik_layer_emit_update ( VIK_LAYER(vtl) );
9304 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9306 if ( vtl->current_track->trackpoints )
9308 GList *last = g_list_last(vtl->current_track->trackpoints);
9309 g_free ( last->data );
9310 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9313 update_statusbar ( vtl );
9315 vik_layer_emit_update ( VIK_LAYER(vtl) );
9322 * Common function to handle trackpoint button requests on either a route or a track
9323 * . enables adding a point via normal click
9324 * . enables removal of last point via right click
9325 * . finishing of the track or route via double clicking
9327 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9331 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9334 if ( event->button == 2 ) {
9335 // As the display is panning, the new track pixmap is now invalid so don't draw it
9336 // otherwise this drawing done results in flickering back to an old image
9337 vtl->draw_sync_do = FALSE;
9341 if ( event->button == 3 )
9343 if ( !vtl->current_track )
9346 if ( vtl->current_track->trackpoints )
9348 GList *last = g_list_last(vtl->current_track->trackpoints);
9349 g_free ( last->data );
9350 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9352 vik_track_calculate_bounds ( vtl->current_track );
9353 update_statusbar ( vtl );
9355 vik_layer_emit_update ( VIK_LAYER(vtl) );
9359 if ( event->type == GDK_2BUTTON_PRESS )
9361 /* subtract last (duplicate from double click) tp then end */
9362 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9364 GList *last = g_list_last(vtl->current_track->trackpoints);
9365 g_free ( last->data );
9366 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9367 /* undo last, then end */
9368 vtl->current_track = NULL;
9370 vik_layer_emit_update ( VIK_LAYER(vtl) );
9374 tp = vik_trackpoint_new();
9375 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9377 /* snap to other TP */
9378 if ( event->state & GDK_CONTROL_MASK )
9380 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9382 tp->coord = other_tp->coord;
9385 tp->newsegment = FALSE;
9386 tp->has_timestamp = FALSE;
9389 if ( vtl->current_track ) {
9390 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9391 /* Auto attempt to get elevation from DEM data (if it's available) */
9392 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9395 vtl->ct_x1 = vtl->ct_x2;
9396 vtl->ct_y1 = vtl->ct_y2;
9397 vtl->ct_x2 = event->x;
9398 vtl->ct_y2 = event->y;
9400 vik_layer_emit_update ( VIK_LAYER(vtl) );
9404 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9406 // ----------------------------------------------------- if current is a route - switch to new track
9407 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9409 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9410 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9412 new_track_create_common ( vtl, name );
9418 return tool_new_track_or_route_click ( vtl, event, vvp );
9421 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9423 if ( event->button == 2 ) {
9424 // Pan moving ended - enable potential point drawing again
9425 vtl->draw_sync_do = TRUE;
9426 vtl->draw_sync_done = TRUE;
9430 /*** New route ****/
9432 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9437 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9439 // -------------------------- if current is a track - switch to new route
9440 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9442 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9443 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9444 new_route_create_common ( vtl, name );
9450 return tool_new_track_or_route_click ( vtl, event, vvp );
9453 /*** New waypoint ****/
9455 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9460 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9463 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9465 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9466 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9467 trw_layer_calculate_bounds_waypoints ( vtl );
9468 vik_layer_emit_update ( VIK_LAYER(vtl) );
9474 /*** Edit trackpoint ****/
9476 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9478 tool_ed_t *t = g_new(tool_ed_t, 1);
9484 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9489 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9491 tool_ed_t *t = data;
9492 VikViewport *vvp = t->vvp;
9493 TPSearchParams params;
9494 /* OUTDATED DOCUMENTATION:
9495 find 5 pixel range on each side. then put these UTM, and a pointer
9496 to the winning track name (and maybe the winning track itself), and a
9497 pointer to the winning trackpoint, inside an array or struct. pass
9498 this along, do a foreach on the tracks which will do a foreach on the
9501 params.x = event->x;
9502 params.y = event->y;
9503 params.closest_track_id = NULL;
9504 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9505 params.closest_tp = NULL;
9506 params.closest_tpl = NULL;
9507 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9509 if ( event->button != 1 )
9512 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9515 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9518 if ( vtl->current_tpl )
9520 /* 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.) */
9521 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9522 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9527 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9529 if ( current_tr->visible &&
9530 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9531 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9532 marker_begin_move ( t, event->x, event->y );
9538 if ( vtl->tracks_visible )
9539 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9541 if ( params.closest_tp )
9543 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9544 vtl->current_tpl = params.closest_tpl;
9545 vtl->current_tp_id = params.closest_track_id;
9546 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9547 trw_layer_tpwin_init ( vtl );
9548 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9549 vik_layer_emit_update ( VIK_LAYER(vtl) );
9553 if ( vtl->routes_visible )
9554 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
9556 if ( params.closest_tp )
9558 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9559 vtl->current_tpl = params.closest_tpl;
9560 vtl->current_tp_id = params.closest_track_id;
9561 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9562 trw_layer_tpwin_init ( vtl );
9563 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9564 vik_layer_emit_update ( VIK_LAYER(vtl) );
9568 /* these aren't the droids you're looking for */
9572 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9574 tool_ed_t *t = data;
9575 VikViewport *vvp = t->vvp;
9577 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9583 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9586 if ( event->state & GDK_CONTROL_MASK )
9588 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9589 if ( tp && tp != vtl->current_tpl->data )
9590 new_coord = tp->coord;
9592 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9595 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9596 marker_moveto ( t, x, y );
9604 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9606 tool_ed_t *t = data;
9607 VikViewport *vvp = t->vvp;
9609 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9611 if ( event->button != 1)
9616 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9619 if ( event->state & GDK_CONTROL_MASK )
9621 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9622 if ( tp && tp != vtl->current_tpl->data )
9623 new_coord = tp->coord;
9626 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9627 if ( vtl->current_tp_track )
9628 vik_track_calculate_bounds ( vtl->current_tp_track );
9630 marker_end_move ( t );
9632 /* diff dist is diff from orig */
9634 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9636 vik_layer_emit_update ( VIK_LAYER(vtl) );
9643 /*** Route Finder ***/
9644 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9649 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9652 if ( !vtl ) return FALSE;
9653 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9654 if ( event->button == 3 && vtl->route_finder_current_track ) {
9656 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9658 vtl->route_finder_coord = *new_end;
9660 vik_layer_emit_update ( VIK_LAYER(vtl) );
9661 /* remove last ' to:...' */
9662 if ( vtl->route_finder_current_track->comment ) {
9663 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9664 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9665 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9666 last_to - vtl->route_finder_current_track->comment - 1);
9667 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9672 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9673 struct LatLon start, end;
9675 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9676 vik_coord_to_latlon ( &(tmp), &end );
9677 vtl->route_finder_coord = tmp; /* for continuations */
9679 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9680 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9681 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9683 vtl->route_finder_check_added_track = TRUE;
9684 vtl->route_finder_started = FALSE;
9687 vik_routing_default_find ( vtl, start, end);
9689 /* see if anything was done -- a track was added or appended to */
9690 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9691 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 ) );
9692 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9693 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9694 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9695 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9698 if ( vtl->route_finder_added_track )
9699 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9701 vtl->route_finder_added_track = NULL;
9702 vtl->route_finder_check_added_track = FALSE;
9703 vtl->route_finder_append = FALSE;
9705 vik_layer_emit_update ( VIK_LAYER(vtl) );
9707 vtl->route_finder_started = TRUE;
9708 vtl->route_finder_coord = tmp;
9709 vtl->route_finder_current_track = NULL;
9714 /*** Show picture ****/
9716 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9721 /* Params are: vvp, event, last match found or NULL */
9722 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9724 if ( wp->image && wp->visible )
9726 gint x, y, slackx, slacky;
9727 GdkEventButton *event = (GdkEventButton *) params[1];
9729 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9730 slackx = wp->image_width / 2;
9731 slacky = wp->image_height / 2;
9732 if ( x <= event->x + slackx && x >= event->x - slackx
9733 && y <= event->y + slacky && y >= event->y - slacky )
9735 params[2] = wp->image; /* we've found a match. however continue searching
9736 * since we want to find the last match -- that
9737 * is, the match that was drawn last. */
9742 static void trw_layer_show_picture ( gpointer pass_along[6] )
9744 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9746 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
9749 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
9750 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
9751 g_free ( quoted_file );
9752 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9754 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() );
9755 g_error_free ( err );
9758 #endif /* WINDOWS */
9761 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9763 gpointer params[3] = { vvp, event, NULL };
9764 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9766 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9769 static gpointer pass_along[6];
9770 pass_along[0] = vtl;
9771 pass_along[5] = params[2];
9772 trw_layer_show_picture ( pass_along );
9773 return TRUE; /* found a match */
9776 return FALSE; /* go through other layers, searching for a match */
9779 /***************************************************************************
9781 ***************************************************************************/
9784 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
9786 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9787 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9790 /* Structure for thumbnail creating data used in the background thread */
9792 VikTrwLayer *vtl; // Layer needed for redrawing
9793 GSList *pics; // Image list
9794 } thumbnail_create_thread_data;
9796 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
9798 guint total = g_slist_length(tctd->pics), done = 0;
9799 while ( tctd->pics )
9801 a_thumbnails_create ( (gchar *) tctd->pics->data );
9802 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9804 return -1; /* Abort thread */
9806 tctd->pics = tctd->pics->next;
9809 // Redraw to show the thumbnails as they are now created
9810 if ( IS_VIK_LAYER(tctd->vtl) )
9811 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
9816 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
9818 while ( tctd->pics )
9820 g_free ( tctd->pics->data );
9821 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
9826 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
9828 if ( ! vtl->has_verified_thumbnails )
9830 GSList *pics = NULL;
9831 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9834 gint len = g_slist_length ( pics );
9835 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
9836 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9839 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9841 (vik_thr_func) create_thumbnails_thread,
9843 (vik_thr_free_func) thumbnail_create_thread_free,
9851 static const gchar* my_track_colors ( gint ii )
9853 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9865 // Fast and reliable way of returning a colour
9866 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9869 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9871 GHashTableIter iter;
9872 gpointer key, value;
9876 g_hash_table_iter_init ( &iter, vtl->tracks );
9878 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9880 // Tracks get a random spread of colours if not already assigned
9881 if ( ! VIK_TRACK(value)->has_color ) {
9882 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9883 VIK_TRACK(value)->color = vtl->track_color;
9885 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9887 VIK_TRACK(value)->has_color = TRUE;
9890 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9893 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9899 g_hash_table_iter_init ( &iter, vtl->routes );
9901 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9903 // Routes get an intermix of reds
9904 if ( ! VIK_TRACK(value)->has_color ) {
9906 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9908 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
9909 VIK_TRACK(value)->has_color = TRUE;
9912 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
9919 * (Re)Calculate the bounds of the waypoints in this layer,
9920 * This should be called whenever waypoints are changed
9922 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
9924 struct LatLon topleft = { 0.0, 0.0 };
9925 struct LatLon bottomright = { 0.0, 0.0 };
9928 GHashTableIter iter;
9929 gpointer key, value;
9931 g_hash_table_iter_init ( &iter, vtl->waypoints );
9933 // Set bounds to first point
9934 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9935 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9936 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9939 // Ensure there is another point...
9940 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9942 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9944 // See if this point increases the bounds.
9945 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9947 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9948 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9949 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9950 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9954 vtl->waypoints_bbox.north = topleft.lat;
9955 vtl->waypoints_bbox.east = bottomright.lon;
9956 vtl->waypoints_bbox.south = bottomright.lat;
9957 vtl->waypoints_bbox.west = topleft.lon;
9960 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9962 vik_track_calculate_bounds ( trk );
9965 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9967 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9968 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9971 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9973 if ( ! VIK_LAYER(vtl)->vt )
9976 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9977 if ( g_hash_table_size (vtl->tracks) > 1 )
9978 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9980 if ( g_hash_table_size (vtl->routes) > 1 )
9981 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9983 if ( g_hash_table_size (vtl->waypoints) > 1 )
9984 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9987 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9989 if ( VIK_LAYER(vtl)->realized )
9990 trw_layer_verify_thumbnails ( vtl, vvp );
9991 trw_layer_track_alloc_colors ( vtl );
9993 trw_layer_calculate_bounds_waypoints ( vtl );
9994 trw_layer_calculate_bounds_tracks ( vtl );
9996 // Apply treeview sort after loading all the tracks for this layer
9997 // (rather than sorted insert on each individual track additional)
9998 // and after subsequent changes to the properties as the specified order may have changed.
9999 // since the sorting of a treeview section is now very quick
10000 // NB sorting is also performed after every name change as well to maintain the list order
10001 trw_layer_sort_all ( vtl );
10004 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10006 return vtl->coord_mode;
10010 * Uniquify the whole layer
10011 * Also requires the layers panel as the names shown there need updating too
10012 * Returns whether the operation was successful or not
10014 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10016 if ( vtl && vlp ) {
10017 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10018 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10019 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10025 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10027 vik_coord_convert ( &(wp->coord), *dest_mode );
10030 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10032 vik_track_convert ( tr, *dest_mode );
10035 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10037 if ( vtl->coord_mode != dest_mode )
10039 vtl->coord_mode = dest_mode;
10040 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10041 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10042 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10046 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10048 vtl->menu_selection = selection;
10051 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10053 return (vtl->menu_selection);
10056 /* ----------- Downloading maps along tracks --------------- */
10058 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10060 /* TODO: calculating based on current size of viewport */
10061 const gdouble w_at_zoom_0_125 = 0.0013;
10062 const gdouble h_at_zoom_0_125 = 0.0011;
10063 gdouble zoom_factor = zoom_level/0.125;
10065 wh->lat = h_at_zoom_0_125 * zoom_factor;
10066 wh->lon = w_at_zoom_0_125 * zoom_factor;
10068 return 0; /* all OK */
10071 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10073 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10074 (dist->lat >= ABS(to->north_south - from->north_south)))
10077 VikCoord *coord = g_malloc(sizeof(VikCoord));
10078 coord->mode = VIK_COORD_LATLON;
10080 if (ABS(gradient) < 1) {
10081 if (from->east_west > to->east_west)
10082 coord->east_west = from->east_west - dist->lon;
10084 coord->east_west = from->east_west + dist->lon;
10085 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10087 if (from->north_south > to->north_south)
10088 coord->north_south = from->north_south - dist->lat;
10090 coord->north_south = from->north_south + dist->lat;
10091 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10097 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10099 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10100 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10102 VikCoord *next = from;
10104 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10106 list = g_list_prepend(list, next);
10112 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10114 typedef struct _Rect {
10119 #define GLRECT(iter) ((Rect *)((iter)->data))
10122 GList *rects_to_download = NULL;
10125 if (get_download_area_width(vvp, zoom_level, &wh))
10128 GList *iter = tr->trackpoints;
10132 gboolean new_map = TRUE;
10133 VikCoord *cur_coord, tl, br;
10136 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10138 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10139 rect = g_malloc(sizeof(Rect));
10142 rect->center = *cur_coord;
10143 rects_to_download = g_list_prepend(rects_to_download, rect);
10148 gboolean found = FALSE;
10149 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10150 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10161 GList *fillins = NULL;
10162 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10163 /* seems that ATM the function get_next_coord works only for LATLON */
10164 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10165 /* fill-ins for far apart points */
10166 GList *cur_rect, *next_rect;
10167 for (cur_rect = rects_to_download;
10168 (next_rect = cur_rect->next) != NULL;
10169 cur_rect = cur_rect->next) {
10170 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10171 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10172 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10176 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10179 GList *iter = fillins;
10181 cur_coord = (VikCoord *)(iter->data);
10182 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10183 rect = g_malloc(sizeof(Rect));
10186 rect->center = *cur_coord;
10187 rects_to_download = g_list_prepend(rects_to_download, rect);
10192 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10193 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10197 for (iter = fillins; iter; iter = iter->next)
10198 g_free(iter->data);
10199 g_list_free(fillins);
10201 if (rects_to_download) {
10202 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10203 g_free(rect_iter->data);
10204 g_list_free(rects_to_download);
10208 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
10212 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10213 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10214 gint selected_zoom, default_zoom;
10216 VikTrwLayer *vtl = pass_along[0];
10217 VikLayersPanel *vlp = pass_along[1];
10219 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10220 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
10222 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
10226 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10228 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10229 int num_maps = g_list_length(vmls);
10232 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10236 // Convert from list of vmls to list of names. Allowing the user to select one of them
10237 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10238 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10240 gchar **np = map_names;
10241 VikMapsLayer **lp = map_layers;
10243 for (i = 0; i < num_maps; i++) {
10244 vml = (VikMapsLayer *)(vmls->data);
10246 *np++ = vik_maps_layer_get_map_label(vml);
10249 // Mark end of the array lists
10253 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10254 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10255 if (cur_zoom == zoom_vals[default_zoom])
10258 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10260 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10263 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10266 for (i = 0; i < num_maps; i++)
10267 g_free(map_names[i]);
10269 g_free(map_layers);
10275 /**** lowest waypoint number calculation ***/
10276 static gint highest_wp_number_name_to_number(const gchar *name) {
10277 if ( strlen(name) == 3 ) {
10278 int n = atoi(name);
10279 if ( n < 100 && name[0] != '0' )
10281 if ( n < 10 && name[0] != '0' )
10289 static void highest_wp_number_reset(VikTrwLayer *vtl)
10291 vtl->highest_wp_number = -1;
10294 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10296 /* if is bigger that top, add it */
10297 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10298 if ( new_wp_num > vtl->highest_wp_number )
10299 vtl->highest_wp_number = new_wp_num;
10302 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10304 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10305 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10306 if ( vtl->highest_wp_number == old_wp_num ) {
10308 vtl->highest_wp_number--;
10310 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10311 /* search down until we find something that *does* exist */
10313 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10314 vtl->highest_wp_number--;
10315 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10320 /* get lowest unused number */
10321 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10324 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10326 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10327 return g_strdup(buf);
10331 * trw_layer_create_track_list_both:
10333 * Create the latest list of tracks and routes
10335 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10337 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10338 GList *tracks = NULL;
10339 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10340 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10342 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10345 static void trw_layer_track_list_dialog_single ( gpointer pass_along[6] )
10347 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
10349 gchar *title = NULL;
10350 if ( GPOINTER_TO_INT(pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10351 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10353 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10355 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), pass_along[2], trw_layer_create_track_list, FALSE );
10359 static void trw_layer_track_list_dialog ( gpointer lav[2] )
10361 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10362 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10364 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10365 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10369 static void trw_layer_waypoint_list_dialog ( gpointer lav[2] )
10371 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
10372 //VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
10374 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10375 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );