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-2015, 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_export.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_wpwin.h"
39 #include "viktrwlayer_propwin.h"
40 #include "viktrwlayer_analysis.h"
41 #include "viktrwlayer_tracklist.h"
42 #include "viktrwlayer_waypointlist.h"
43 #ifdef VIK_CONFIG_GEOTAG
44 #include "viktrwlayer_geotag.h"
45 #include "geotag_exif.h"
47 #include "garminsymbols.h"
48 #include "thumbnails.h"
49 #include "background.h"
55 #include "geonamessearch.h"
56 #ifdef VIK_CONFIG_OPENSTREETMAP
57 #include "osm-traces.h"
60 #include "datasources.h"
61 #include "datasource_gps.h"
62 #include "vikexttool_datasources.h"
66 #include "vikrouting.h"
68 #include "icons/icons.h"
82 #include <gdk/gdkkeysyms.h>
84 #include <glib/gstdio.h>
85 #include <glib/gi18n.h>
87 #define VIK_TRW_LAYER_TRACK_GC 6
88 #define VIK_TRW_LAYER_TRACK_GCS 10
89 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
90 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
91 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
92 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
93 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
94 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
96 #define DRAWMODE_BY_TRACK 0
97 #define DRAWMODE_BY_SPEED 1
98 #define DRAWMODE_ALL_SAME_COLOR 2
99 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
100 // as we are (re)calculating the colour for every point
105 /* this is how it knows when you click if you are clicking close to a trackpoint. */
106 #define TRACKPOINT_SIZE_APPROX 5
107 #define WAYPOINT_SIZE_APPROX 5
109 #define MIN_STOP_LENGTH 15
110 #define MAX_STOP_LENGTH 86400
111 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
112 /* this is multiplied by user-inputted value from 1-100. */
114 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
116 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
118 FS_XX_SMALL = 0, // 'xx-small'
121 FS_MEDIUM, // DEFAULT
128 struct _VikTrwLayer {
131 GHashTable *tracks_iters;
133 GHashTable *routes_iters;
134 GHashTable *waypoints_iters;
135 GHashTable *waypoints;
136 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
137 gboolean tracks_visible, routes_visible, waypoints_visible;
138 LatLonBBox waypoints_bbox;
140 gboolean track_draw_labels;
143 guint8 drawpoints_size;
144 guint8 drawelevation;
145 guint8 elevation_factor;
149 guint8 drawdirections;
150 guint8 drawdirections_size;
151 guint8 line_thickness;
152 guint8 bg_line_thickness;
153 vik_layer_sort_order_t track_sort_order;
156 VikTRWMetadata *metadata;
158 PangoLayout *tracklabellayout;
159 font_size_t track_font_size;
160 gchar *track_fsize_str;
164 gboolean wp_draw_symbols;
165 font_size_t wp_font_size;
167 vik_layer_sort_order_t wp_sort_order;
169 gdouble track_draw_speed_factor;
171 GdkGC *track_1color_gc;
172 GdkColor track_color;
173 GdkGC *current_track_gc;
174 // Separate GC for a track's potential new point as drawn via separate method
175 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
176 GdkGC *current_track_newpoint_gc;
177 GdkGC *track_bg_gc; GdkColor track_bg_color;
178 GdkGC *waypoint_gc; GdkColor waypoint_color;
179 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
180 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
182 GdkFont *waypoint_font;
183 VikTrack *current_track; // ATM shared between new tracks and new routes
184 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
185 gboolean draw_sync_done;
186 gboolean draw_sync_do;
188 VikCoordMode coord_mode;
190 /* wp editing tool */
191 VikWaypoint *current_wp;
192 gpointer current_wp_id;
194 gboolean waypoint_rightclick;
196 /* track editing tool */
198 VikTrack *current_tp_track;
199 gpointer current_tp_id;
200 VikTrwLayerTpwin *tpwin;
202 /* track editing tool -- more specifically, moving tps */
205 /* route finder tool */
206 gboolean route_finder_started;
207 gboolean route_finder_check_added_track;
208 VikTrack *route_finder_added_track;
209 gboolean route_finder_append;
216 guint16 image_cache_size;
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
221 gboolean has_verified_thumbnails;
223 GtkMenu *wp_right_click_menu;
224 GtkMenu *track_right_click_menu;
227 VikStdLayerMenuItem menu_selection;
229 gint highest_wp_number;
232 GtkWidget *tracks_analysis_dialog;
235 /* A caached waypoint image. */
238 gchar *image; /* filename */
241 struct DrawingParams {
246 guint16 width, height;
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
249 const VikCoord *center;
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
256 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
261 MA_SUBTYPE, // OR END for Layer only
270 typedef gpointer menu_array_layer[2];
271 typedef gpointer menu_array_sublayer[MA_LAST];
273 static void trw_layer_delete_item ( menu_array_sublayer values );
274 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
277 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
278 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
280 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
283 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
286 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
287 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292 static void trw_layer_goto_track_center ( menu_array_sublayer values );
293 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295 static void trw_layer_merge_with_other ( menu_array_sublayer values );
296 static void trw_layer_append_track ( menu_array_sublayer values );
297 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300 static void trw_layer_split_segments ( menu_array_sublayer values );
301 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304 static void trw_layer_reverse ( menu_array_sublayer values );
305 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307 static void trw_layer_show_picture ( menu_array_sublayer values );
308 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
310 static void trw_layer_centerize ( menu_array_layer values );
311 static void trw_layer_auto_view ( menu_array_layer values );
312 static void trw_layer_goto_wp ( menu_array_layer values );
313 static void trw_layer_new_wp ( menu_array_layer values );
314 static void trw_layer_new_track ( menu_array_layer values );
315 static void trw_layer_new_route ( menu_array_layer values );
316 static void trw_layer_finish_track ( menu_array_layer values );
317 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318 static void trw_layer_auto_tracks_view ( menu_array_layer values );
319 static void trw_layer_delete_all_tracks ( menu_array_layer values );
320 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
325 #ifdef VIK_CONFIG_GEOTAG
326 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328 static void trw_layer_geotagging_track ( menu_array_sublayer values );
329 static void trw_layer_geotagging ( menu_array_layer values );
331 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333 static void trw_layer_acquire_url_cb ( menu_array_layer values );
334 #ifdef VIK_CONFIG_OPENSTREETMAP
335 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
338 #ifdef VIK_CONFIG_GEOCACHES
339 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
341 #ifdef VIK_CONFIG_GEOTAG
342 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
344 static void trw_layer_acquire_file_cb ( menu_array_layer values );
345 static void trw_layer_gps_upload ( menu_array_layer values );
347 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
348 static void trw_layer_track_list_dialog ( menu_array_layer values );
349 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
351 // Specific route versions:
352 // Most track handling functions can handle operating on the route list
353 // However these ones are easier in separate functions
354 static void trw_layer_auto_routes_view ( menu_array_layer values );
355 static void trw_layer_delete_all_routes ( menu_array_layer values );
356 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
359 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
364 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
365 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
367 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
372 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
379 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
388 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
389 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
391 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
397 static void cached_pixbuf_free ( CachedPixbuf *cp );
398 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
400 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
403 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
404 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
406 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
407 static void highest_wp_number_reset(VikTrwLayer *vtl);
408 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
409 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
411 // Note for the following tool GtkRadioActionEntry texts:
412 // the very first text value is an internal name not displayed anywhere
413 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
414 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
415 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
416 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
417 static VikToolInterface trw_layer_tools[] = {
418 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
419 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
420 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
422 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
424 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
425 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
426 (VikToolMouseFunc) tool_new_track_click,
427 (VikToolMouseMoveFunc) tool_new_track_move,
428 (VikToolMouseFunc) tool_new_track_release,
429 (VikToolKeyFunc) tool_new_track_key_press,
430 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
431 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
433 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
434 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
435 (VikToolMouseFunc) tool_new_route_click,
436 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
437 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
438 (VikToolKeyFunc) tool_new_track_key_press, // -/#
439 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
440 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
442 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
443 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
444 (VikToolMouseFunc) tool_extended_route_finder_click,
445 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
446 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
447 (VikToolKeyFunc) tool_extended_route_finder_key_press,
448 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
449 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
451 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
452 (VikToolConstructorFunc) tool_edit_waypoint_create,
453 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
455 (VikToolMouseFunc) tool_edit_waypoint_click,
456 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
457 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
459 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
461 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
462 (VikToolConstructorFunc) tool_edit_trackpoint_create,
463 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
465 (VikToolMouseFunc) tool_edit_trackpoint_click,
466 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
467 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
469 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
471 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
472 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
473 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
475 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
480 TOOL_CREATE_WAYPOINT=0,
485 TOOL_EDIT_TRACKPOINT,
490 /****** PARAMETERS ******/
492 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
493 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
495 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
496 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
498 #define MIN_POINT_SIZE 2
499 #define MAX_POINT_SIZE 10
501 #define MIN_ARROW_SIZE 3
502 #define MAX_ARROW_SIZE 20
504 static VikLayerParamScale params_scales[] = {
505 /* min max step digits */
506 { 1, 10, 1, 0 }, /* line_thickness */
507 { 0, 100, 1, 0 }, /* track draw speed factor */
508 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
509 /* 5 * step == how much to turn */
510 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
511 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
512 { 5, 500, 5, 0 }, // 5: image cache_size - " "
513 { 0, 8, 1, 0 }, // 6: Background line thickness
514 { 1, 64, 1, 0 }, /* wpsize */
515 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
516 { 1, 100, 1, 0 }, // 9: elevation factor
517 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
518 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
521 static gchar* params_font_sizes[] = {
522 N_("Extra Extra Small"),
528 N_("Extra Extra Large"),
531 // Needs to align with vik_layer_sort_order_t
532 static gchar* params_sort_order[] = {
534 N_("Name Ascending"),
535 N_("Name Descending"),
536 N_("Date Ascending"),
537 N_("Date Descending"),
541 static VikLayerParamData black_color_default ( void ) {
542 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
544 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
545 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
546 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
547 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
548 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
549 static VikLayerParamData trackbgcolor_default ( void ) {
550 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
552 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
553 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
554 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
556 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
557 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
558 static VikLayerParamData wptextcolor_default ( void ) {
559 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
561 static VikLayerParamData wpbgcolor_default ( void ) {
562 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
564 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
565 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
567 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
568 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
569 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
571 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
573 static VikLayerParamData string_default ( void )
575 VikLayerParamData data;
580 VikLayerParam trw_layer_params[] = {
581 { 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 },
582 { 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 },
583 { 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 },
585 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
586 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
587 { 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 },
588 { 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 },
589 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
590 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
591 { 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 },
592 { 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 },
593 { 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 },
594 { 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 },
595 { 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 },
596 { 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 },
597 { 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 },
598 { 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 },
599 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
600 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 },
601 { 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 },
603 { 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 },
604 { 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 },
605 { 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,
606 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
607 { 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 },
609 { 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 },
610 { 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 },
611 { 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 },
612 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
613 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
614 { 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 },
615 { 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 },
616 { 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 },
617 { 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 },
618 { 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 },
620 { 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 },
621 { 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 },
622 { 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 },
623 { 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 },
625 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
626 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
627 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
628 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
631 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
633 // Sublayer visibilities
681 *** 1) Add to trw_layer_params and enumeration
682 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
685 /****** END PARAMETERS ******/
687 /* Layer Interface function definitions */
688 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
689 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
690 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
691 static void trw_layer_free ( VikTrwLayer *trwlayer );
692 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
693 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
694 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl );
695 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
696 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
697 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
698 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
699 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
700 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
701 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
702 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
703 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
704 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
705 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
706 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
707 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
708 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
709 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
710 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
711 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
712 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
713 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
714 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
715 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
716 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
717 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
718 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
719 /* End Layer Interface function definitions */
721 VikLayerInterface vik_trw_layer_interface = {
728 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
732 params_groups, /* params_groups */
733 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
737 (VikLayerFuncCreate) trw_layer_create,
738 (VikLayerFuncRealize) trw_layer_realize,
739 (VikLayerFuncPostRead) trw_layer_post_read,
740 (VikLayerFuncFree) trw_layer_free,
742 (VikLayerFuncProperties) NULL,
743 (VikLayerFuncDraw) trw_layer_draw,
744 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
745 (VikLayerFuncGetTimestamp) trw_layer_get_timestamp,
747 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
748 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
750 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
751 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
753 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
754 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
755 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
756 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
757 (VikLayerFuncLayerSelected) trw_layer_selected,
759 (VikLayerFuncMarshall) trw_layer_marshall,
760 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
762 (VikLayerFuncSetParam) trw_layer_set_param,
763 (VikLayerFuncGetParam) trw_layer_get_param,
764 (VikLayerFuncChangeParam) trw_layer_change_param,
766 (VikLayerFuncReadFileData) a_gpspoint_read_file,
767 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
769 (VikLayerFuncDeleteItem) trw_layer_del_item,
770 (VikLayerFuncCutItem) trw_layer_cut_item,
771 (VikLayerFuncCopyItem) trw_layer_copy_item,
772 (VikLayerFuncPasteItem) trw_layer_paste_item,
773 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
775 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
777 (VikLayerFuncSelectClick) trw_layer_select_click,
778 (VikLayerFuncSelectMove) trw_layer_select_move,
779 (VikLayerFuncSelectRelease) trw_layer_select_release,
780 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
783 static gboolean have_diary_program = FALSE;
784 static gchar *diary_program = NULL;
785 #define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
787 static gboolean have_geojson_export = FALSE;
789 static gboolean have_astro_program = FALSE;
790 static gchar *astro_program = NULL;
791 #define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
793 // NB Only performed once per program run
794 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
796 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
798 //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
799 diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
801 diary_program = g_strdup ( "rednotebook" );
805 // User specified so assume it works
806 have_diary_program = TRUE;
809 if ( g_find_program_in_path( diary_program ) ) {
810 gchar *mystdout = NULL;
811 gchar *mystderr = NULL;
812 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
813 gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version"
814 if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) {
815 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
817 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
819 g_warning ("Diary: stderr: %s", mystderr );
821 gchar **tokens = NULL;
822 if ( mystdout && g_strcmp0(mystdout, "") )
823 tokens = g_strsplit(mystdout, " ", 0);
825 tokens = g_strsplit(mystderr, " ", 0);
828 gchar *token = tokens[num];
829 while ( token && num < 2 ) {
831 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
832 have_diary_program = TRUE;
837 g_strfreev ( tokens );
844 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
845 have_geojson_export = TRUE;
849 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
851 //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
852 astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
854 astro_program = g_strdup ( "stellarium" );
858 // User specified so assume it works
859 have_astro_program = TRUE;
861 if ( g_find_program_in_path( astro_program ) ) {
862 have_astro_program = TRUE;
866 GType vik_trw_layer_get_type ()
868 static GType vtl_type = 0;
872 static const GTypeInfo vtl_info =
874 sizeof (VikTrwLayerClass),
875 NULL, /* base_init */
876 NULL, /* base_finalize */
877 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
878 NULL, /* class_finalize */
879 NULL, /* class_data */
880 sizeof (VikTrwLayer),
882 NULL /* instance init */
884 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
889 VikTRWMetadata *vik_trw_metadata_new()
891 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
894 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
899 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
901 return vtl->metadata;
904 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
907 vik_trw_metadata_free ( vtl->metadata );
908 vtl->metadata = metadata;
913 const gchar *date_str;
915 const VikWaypoint *wpt;
920 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
924 // Might be an easier way to compare dates rather than converting the strings all the time...
925 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
926 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
928 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
937 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
941 // Might be an easier way to compare dates rather than converting the strings all the time...
942 if ( wpt->has_timestamp ) {
943 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
945 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
955 * Find an item by date
957 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
961 df.date_str = date_str;
966 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
968 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
970 if ( select && df.found ) {
971 if ( do_tracks && df.trk ) {
972 struct LatLon maxmin[2] = { {0,0}, {0,0} };
973 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
974 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
975 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
978 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
979 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
981 vik_layer_emit_update ( VIK_LAYER(vtl) );
986 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
988 static menu_array_sublayer values;
994 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
997 values[MA_VTL] = vtl;
998 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
999 values[MA_SUBLAYER_ID] = sublayer;
1000 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
1002 trw_layer_delete_item ( values );
1005 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
1007 static menu_array_sublayer values;
1013 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
1016 values[MA_VTL] = vtl;
1017 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
1018 values[MA_SUBLAYER_ID] = sublayer;
1019 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
1021 trw_layer_copy_item_cb(values);
1022 trw_layer_cut_item_cb(values);
1025 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
1027 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1028 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
1029 gpointer * sublayer = values[MA_SUBLAYER_ID];
1030 guint8 *data = NULL;
1033 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
1037 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1038 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
1039 if ( wp && wp->name )
1042 name = NULL; // Broken :(
1044 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1045 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1046 if ( trk && trk->name )
1049 name = NULL; // Broken :(
1052 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1053 if ( trk && trk->name )
1056 name = NULL; // Broken :(
1059 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1060 subtype, len, name, data);
1064 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1066 trw_layer_copy_item_cb(values);
1067 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1068 trw_layer_delete_item(values);
1071 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1073 // Slightly cheating method, routing via the panels capability
1074 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1077 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1087 GByteArray *ba = g_byte_array_new ();
1089 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1090 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1091 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1092 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1094 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1097 g_byte_array_append ( ba, id, il );
1105 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1112 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1116 w = vik_waypoint_unmarshall ( item, len );
1117 // When copying - we'll create a new name based on the original
1118 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1119 vik_trw_layer_add_waypoint ( vtl, name, w );
1120 waypoint_convert (NULL, w, &vtl->coord_mode);
1123 trw_layer_calculate_bounds_waypoints ( vtl );
1125 // Consider if redraw necessary for the new item
1126 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1127 vik_layer_emit_update ( VIK_LAYER(vtl) );
1130 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1134 t = vik_track_unmarshall ( item, len );
1135 // When copying - we'll create a new name based on the original
1136 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1137 vik_trw_layer_add_track ( vtl, name, t );
1138 vik_track_convert (t, vtl->coord_mode);
1141 // Consider if redraw necessary for the new item
1142 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1143 vik_layer_emit_update ( VIK_LAYER(vtl) );
1146 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1150 t = vik_track_unmarshall ( item, len );
1151 // When copying - we'll create a new name based on the original
1152 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1153 vik_trw_layer_add_route ( vtl, name, t );
1154 vik_track_convert (t, vtl->coord_mode);
1157 // Consider if redraw necessary for the new item
1158 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1159 vik_layer_emit_update ( VIK_LAYER(vtl) );
1165 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1172 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1176 case PARAM_TV: vtl->tracks_visible = data.b; break;
1177 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1178 case PARAM_RV: vtl->routes_visible = data.b; break;
1179 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1180 case PARAM_TLFONTSIZE:
1181 if ( data.u < FS_NUM_SIZES ) {
1182 vtl->track_font_size = data.u;
1183 g_free ( vtl->track_fsize_str );
1184 switch ( vtl->track_font_size ) {
1185 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1186 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1187 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1188 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1189 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1190 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1191 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1195 case PARAM_DM: vtl->drawmode = data.u; break;
1197 vtl->track_color = data.c;
1198 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1200 case PARAM_DP: vtl->drawpoints = data.b; break;
1202 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1203 vtl->drawpoints_size = data.u;
1205 case PARAM_DE: vtl->drawelevation = data.b; break;
1206 case PARAM_DS: vtl->drawstops = data.b; break;
1207 case PARAM_DL: vtl->drawlines = data.b; break;
1208 case PARAM_DD: vtl->drawdirections = data.b; break;
1210 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1211 vtl->drawdirections_size = data.u;
1213 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1214 vtl->stop_length = data.u;
1216 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1217 vtl->elevation_factor = data.u;
1219 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1221 vtl->line_thickness = data.u;
1222 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1225 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1227 vtl->bg_line_thickness = data.u;
1228 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1232 vtl->track_bg_color = data.c;
1233 if ( vtl->track_bg_gc )
1234 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1236 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1237 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1238 case PARAM_DLA: vtl->drawlabels = data.b; break;
1239 case PARAM_DI: vtl->drawimages = data.b; break;
1240 case PARAM_IS: if ( data.u != vtl->image_size )
1242 vtl->image_size = data.u;
1243 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1244 g_queue_free ( vtl->image_cache );
1245 vtl->image_cache = g_queue_new ();
1248 case PARAM_IA: vtl->image_alpha = data.u; break;
1249 case PARAM_ICS: vtl->image_cache_size = data.u;
1250 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1251 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1254 vtl->waypoint_color = data.c;
1255 if ( vtl->waypoint_gc )
1256 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1259 vtl->waypoint_text_color = data.c;
1260 if ( vtl->waypoint_text_gc )
1261 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1264 vtl->waypoint_bg_color = data.c;
1265 if ( vtl->waypoint_bg_gc )
1266 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1269 vtl->wpbgand = data.b;
1270 if ( vtl->waypoint_bg_gc )
1271 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1273 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1274 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1275 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1276 case PARAM_WPFONTSIZE:
1277 if ( data.u < FS_NUM_SIZES ) {
1278 vtl->wp_font_size = data.u;
1279 g_free ( vtl->wp_fsize_str );
1280 switch ( vtl->wp_font_size ) {
1281 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1282 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1283 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1284 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1285 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1286 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1287 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1291 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1293 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1294 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1295 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1296 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1302 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1304 VikLayerParamData rv;
1307 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1308 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1309 case PARAM_RV: rv.b = vtl->routes_visible; break;
1310 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1311 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1312 case PARAM_DM: rv.u = vtl->drawmode; break;
1313 case PARAM_TC: rv.c = vtl->track_color; break;
1314 case PARAM_DP: rv.b = vtl->drawpoints; break;
1315 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1316 case PARAM_DE: rv.b = vtl->drawelevation; break;
1317 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1318 case PARAM_DS: rv.b = vtl->drawstops; break;
1319 case PARAM_SL: rv.u = vtl->stop_length; break;
1320 case PARAM_DL: rv.b = vtl->drawlines; break;
1321 case PARAM_DD: rv.b = vtl->drawdirections; break;
1322 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1323 case PARAM_LT: rv.u = vtl->line_thickness; break;
1324 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1325 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1326 case PARAM_DI: rv.b = vtl->drawimages; break;
1327 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1328 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1329 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1330 case PARAM_IS: rv.u = vtl->image_size; break;
1331 case PARAM_IA: rv.u = vtl->image_alpha; break;
1332 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1333 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1334 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1335 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1336 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1337 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1338 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1339 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1340 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1341 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1343 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1344 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1345 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1346 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1352 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1354 // This '-3' is to account for the first few parameters not in the properties
1355 const gint OFFSET = -3;
1357 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1358 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1361 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1362 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1363 GtkWidget **ww2 = values[UI_CHG_LABELS];
1364 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1365 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1366 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1367 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1368 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1369 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1370 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1371 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1372 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1373 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1374 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1375 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1378 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1381 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1382 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1383 GtkWidget **ww2 = values[UI_CHG_LABELS];
1384 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1385 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1386 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1387 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1388 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1389 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1390 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1391 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1392 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1393 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1394 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1395 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1396 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1397 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1398 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1399 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1402 // Alter sensitivity of all track colours according to the draw track mode.
1405 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1406 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1407 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1408 GtkWidget **ww2 = values[UI_CHG_LABELS];
1409 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1410 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1411 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1412 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1415 case PARAM_MDTIME: {
1416 // Force metadata->timestamp to be always read-only for now.
1417 GtkWidget **ww = values[UI_CHG_WIDGETS];
1418 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1419 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1421 // NB Since other track settings have been split across tabs,
1422 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1427 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1434 // Use byte arrays to store sublayer data
1435 // much like done elsewhere e.g. vik_layer_marshall_params()
1436 GByteArray *ba = g_byte_array_new ( );
1441 guint object_length;
1444 // the length of the item
1445 // the sublayer type of item
1446 // the the actual item
1447 #define tlm_append(object_pointer, size, type) \
1449 object_length = (size); \
1450 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1451 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1452 g_byte_array_append ( ba, (object_pointer), object_length );
1454 // Layer parameters first
1455 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1456 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1457 g_byte_array_append ( ba, pd, pl );
1460 // Now sublayer data
1461 GHashTableIter iter;
1462 gpointer key, value;
1465 g_hash_table_iter_init ( &iter, vtl->waypoints );
1466 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1467 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1468 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1473 g_hash_table_iter_init ( &iter, vtl->tracks );
1474 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1475 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1476 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1481 g_hash_table_iter_init ( &iter, vtl->routes );
1482 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1483 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1484 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1494 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1496 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1498 gint consumed_length;
1500 // First the overall layer parameters
1501 memcpy(&pl, data, sizeof(pl));
1503 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1506 consumed_length = pl;
1507 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1509 #define tlm_size (*(gint *)data)
1510 // See marshalling above for order of how this is written
1512 data += sizeof_len_and_subtype + tlm_size;
1514 // Now the individual sublayers:
1516 while ( *data && consumed_length < len ) {
1517 // Normally four extra bytes at the end of the datastream
1518 // (since it's a GByteArray and that's where it's length is stored)
1519 // So only attempt read when there's an actual block of sublayer data
1520 if ( consumed_length + tlm_size < len ) {
1522 // Reuse pl to read the subtype from the data stream
1523 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1525 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1526 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1527 gchar *name = g_strdup ( trk->name );
1528 vik_trw_layer_add_track ( vtl, name, trk );
1531 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1532 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1533 gchar *name = g_strdup ( wp->name );
1534 vik_trw_layer_add_waypoint ( vtl, name, wp );
1537 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1538 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1539 gchar *name = g_strdup ( trk->name );
1540 vik_trw_layer_add_route ( vtl, name, trk );
1544 consumed_length += tlm_size + sizeof_len_and_subtype;
1547 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1549 // Not stored anywhere else so need to regenerate
1550 trw_layer_calculate_bounds_waypoints ( vtl );
1555 // Keep interesting hash function at least visible
1557 static guint strcase_hash(gconstpointer v)
1559 // 31 bit hash function
1562 gchar s[128]; // malloc is too slow for reading big files
1565 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1566 p[i] = toupper(t[i]);
1572 for (p += 1; *p != '\0'; p++)
1573 h = (h << 5) - h + *p;
1580 // Stick a 1 at the end of the function name to make it more unique
1581 // thus more easily searchable in a simple text editor
1582 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1584 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1585 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1587 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1588 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1590 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1591 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1592 // and with normal PC processing capabilities - it has negligibile performance impact
1593 // This also minimized the amount of rework - as the management of the hash tables already exists.
1595 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1596 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1597 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1599 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1600 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1601 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1602 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1603 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1604 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1606 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1608 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1610 // Param settings that are not available via the GUI
1611 // Force to on after processing params (which defaults them to off with a zero value)
1612 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1614 rv->metadata = vik_trw_metadata_new ();
1615 rv->draw_sync_done = TRUE;
1616 rv->draw_sync_do = TRUE;
1617 // Everything else is 0, FALSE or NULL
1623 static void trw_layer_free ( VikTrwLayer *trwlayer )
1625 g_hash_table_destroy(trwlayer->waypoints);
1626 g_hash_table_destroy(trwlayer->waypoints_iters);
1627 g_hash_table_destroy(trwlayer->tracks);
1628 g_hash_table_destroy(trwlayer->tracks_iters);
1629 g_hash_table_destroy(trwlayer->routes);
1630 g_hash_table_destroy(trwlayer->routes_iters);
1632 /* ODC: replace with GArray */
1633 trw_layer_free_track_gcs ( trwlayer );
1635 if ( trwlayer->wp_right_click_menu )
1636 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1638 if ( trwlayer->track_right_click_menu )
1639 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1641 if ( trwlayer->tracklabellayout != NULL)
1642 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1644 if ( trwlayer->wplabellayout != NULL)
1645 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1647 if ( trwlayer->waypoint_gc != NULL )
1648 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1650 if ( trwlayer->waypoint_text_gc != NULL )
1651 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1653 if ( trwlayer->waypoint_bg_gc != NULL )
1654 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1656 g_free ( trwlayer->wp_fsize_str );
1657 g_free ( trwlayer->track_fsize_str );
1659 if ( trwlayer->tpwin != NULL )
1660 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1662 if ( trwlayer->tracks_analysis_dialog != NULL )
1663 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1665 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1666 g_queue_free ( trwlayer->image_cache );
1669 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1673 dp->highlight = highlight;
1674 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1675 dp->xmpp = vik_viewport_get_xmpp ( vp );
1676 dp->ympp = vik_viewport_get_ympp ( vp );
1677 dp->width = vik_viewport_get_width ( vp );
1678 dp->height = vik_viewport_get_height ( vp );
1679 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1680 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1682 dp->center = vik_viewport_get_center ( vp );
1683 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1684 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1689 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1690 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1691 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1693 dp->ce1 = dp->center->east_west-w2;
1694 dp->ce2 = dp->center->east_west+w2;
1695 dp->cn1 = dp->center->north_south-h2;
1696 dp->cn2 = dp->center->north_south+h2;
1697 } else if ( dp->lat_lon ) {
1698 VikCoord upperleft, bottomright;
1699 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1700 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1701 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1702 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1703 dp->ce1 = upperleft.east_west;
1704 dp->ce2 = bottomright.east_west;
1705 dp->cn1 = bottomright.north_south;
1706 dp->cn2 = upperleft.north_south;
1709 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1713 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1714 * Here a simple traffic like light colour system is used:
1715 * . slow points are red
1716 * . average is yellow
1717 * . fast points are green
1719 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1722 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1723 if ( average_speed > 0 ) {
1724 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1725 if ( rv < low_speed )
1726 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1727 else if ( rv > high_speed )
1728 return VIK_TRW_LAYER_TRACK_GC_FAST;
1730 return VIK_TRW_LAYER_TRACK_GC_AVER;
1733 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1736 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1738 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1739 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1740 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1741 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1745 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1747 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1749 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1750 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1752 // Fallback if parse failure
1753 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1755 g_free ( label_markup );
1757 gint label_x, label_y;
1759 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1761 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1762 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1766 * distance_in_preferred_units:
1767 * @dist: The source distance in standard SI Units (i.e. metres)
1769 * TODO: This is a generic function that could be moved into globals.c or utils.c
1771 * Probably best used if you have a only few conversions to perform.
1772 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1773 * since it will be doing the preference check on each call
1775 * Returns: The distance in the units as specified by the preferences
1777 static gdouble distance_in_preferred_units ( gdouble dist )
1780 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1781 switch (dist_units) {
1782 case VIK_UNITS_DISTANCE_MILES:
1783 mydist = VIK_METERS_TO_MILES(dist);
1785 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1786 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1788 // VIK_UNITS_DISTANCE_KILOMETRES:
1790 mydist = dist/1000.0;
1797 * trw_layer_draw_dist_labels:
1799 * Draw a few labels along a track at nicely seperated distances
1800 * This might slow things down if there's many tracks being displayed with this on.
1802 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1804 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1805 25.0, 40.0, 50.0, 75.0, 100.0,
1806 150.0, 200.0, 250.0, 500.0, 1000.0};
1808 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1810 // Convert to specified unit to find the friendly breakdown value
1811 dist = distance_in_preferred_units ( dist );
1815 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1816 if ( chunksd[i] > dist ) {
1818 dist = chunksd[index];
1823 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1825 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1826 gdouble dist_i = dist * i;
1828 // Convert distance back into metres for use in finding a trackpoint
1829 switch (dist_units) {
1830 case VIK_UNITS_DISTANCE_MILES:
1831 dist_i = VIK_MILES_TO_METERS(dist_i);
1833 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1834 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1836 // VIK_UNITS_DISTANCE_KILOMETRES:
1838 dist_i = dist_i*1000.0;
1842 gdouble dist_current = 0.0;
1843 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1844 gdouble dist_next = 0.0;
1845 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1847 gdouble dist_between_tps = fabs (dist_next - dist_current);
1848 gdouble ratio = 0.0;
1849 // Prevent division by 0 errors
1850 if ( dist_between_tps > 0.0 )
1851 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1853 if ( tp_current && tp_next ) {
1854 // Construct the name based on the distance value
1857 switch (dist_units) {
1858 case VIK_UNITS_DISTANCE_MILES:
1859 units = g_strdup ( _("miles") );
1861 // VIK_UNITS_DISTANCE_KILOMETRES:
1863 units = g_strdup ( _("km") );
1867 // Convert for display
1868 dist_i = distance_in_preferred_units ( dist_i );
1870 // Make the precision of the output related to the unit size.
1872 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1873 else if ( index == 1 )
1874 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1876 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1879 struct LatLon ll_current, ll_next;
1880 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1881 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1883 // positional interpolation
1884 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1885 // but should be good enough over the small scale that I anticipate usage on
1886 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1887 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1889 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1892 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1893 fgcolour = gdk_color_to_string ( &(trk->color) );
1895 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1897 // if highlight mode on, then colour the background in the highlight colour
1899 if ( drawing_highlight )
1900 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1902 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1904 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1906 g_free ( fgcolour );
1907 g_free ( bgcolour );
1914 * trw_layer_draw_track_name_labels:
1916 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1918 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1921 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1922 fgcolour = gdk_color_to_string ( &(trk->color) );
1924 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1926 // if highlight mode on, then colour the background in the highlight colour
1928 if ( drawing_highlight )
1929 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1931 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1933 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1935 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1936 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1937 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1938 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1939 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1940 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1942 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1944 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1947 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1948 // No other labels to draw
1951 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1954 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1957 VikCoord begin_coord = tp_begin->coord;
1958 VikCoord end_coord = tp_end->coord;
1960 gboolean done_start_end = FALSE;
1962 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1963 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1965 // This number can be configured via the settings if you really want to change it
1966 gdouble distance_diff;
1967 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1968 distance_diff = 100.0; // Metres
1970 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1971 // Start and end 'close' together so only draw one label at an average location
1972 gint x1, x2, y1, y2;
1973 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1974 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1976 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1978 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1979 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1982 done_start_end = TRUE;
1986 if ( ! done_start_end ) {
1987 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1988 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1989 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1990 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1991 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1992 g_free ( name_start );
1994 // Don't draw end label if this is the one being created
1995 if ( trk != dp->vtl->current_track ) {
1996 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1997 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1998 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1999 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
2000 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
2001 g_free ( name_end );
2006 g_free ( fgcolour );
2007 g_free ( bgcolour );
2011 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
2013 if ( ! track->visible )
2016 /* TODO: this function is a mess, get rid of any redundancy */
2017 GList *list = track->trackpoints;
2019 gboolean useoldvals = TRUE;
2021 gboolean drawpoints;
2023 gboolean drawelevation;
2024 gdouble min_alt, max_alt, alt_diff = 0;
2026 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
2027 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
2030 if ( dp->vtl->drawelevation )
2032 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
2033 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
2034 alt_diff = max_alt - min_alt;
2037 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
2038 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
2039 trw_layer_draw_track ( id, track, dp, TRUE );
2041 if ( draw_track_outline )
2042 drawpoints = drawstops = FALSE;
2044 drawpoints = dp->vtl->drawpoints;
2045 drawstops = dp->vtl->drawstops;
2048 gboolean drawing_highlight = FALSE;
2049 /* Current track - used for creation */
2050 if ( track == dp->vtl->current_track )
2051 main_gc = dp->vtl->current_track_gc;
2053 if ( dp->highlight ) {
2054 /* Draw all tracks of the layer in special colour
2055 NB this supercedes the drawmode */
2056 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2057 drawing_highlight = TRUE;
2059 if ( !drawing_highlight ) {
2060 // Still need to figure out the gc according to the drawing mode:
2061 switch ( dp->vtl->drawmode ) {
2062 case DRAWMODE_BY_TRACK:
2063 if ( dp->vtl->track_1color_gc )
2064 g_object_unref ( dp->vtl->track_1color_gc );
2065 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2066 main_gc = dp->vtl->track_1color_gc;
2069 // Mostly for DRAWMODE_ALL_SAME_COLOR
2070 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2071 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2078 int x, y, oldx, oldy;
2079 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2081 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2083 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2085 // Draw the first point as something a bit different from the normal points
2086 // ATM it's slightly bigger and a triangle
2088 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2089 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2095 gdouble average_speed = 0.0;
2096 gdouble low_speed = 0.0;
2097 gdouble high_speed = 0.0;
2098 // If necessary calculate these values - which is done only once per track redraw
2099 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2100 // the percentage factor away from the average speed determines transistions between the levels
2101 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2102 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2103 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2106 while ((list = g_list_next(list)))
2108 tp = VIK_TRACKPOINT(list->data);
2109 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2111 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2112 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2113 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2114 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2115 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2117 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2120 * If points are the same in display coordinates, don't draw.
2122 if ( useoldvals && x == oldx && y == oldy )
2124 // Still need to process points to ensure 'stops' are drawn if required
2125 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2126 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2127 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 );
2132 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2133 if ( drawpoints || dp->vtl->drawlines ) {
2134 // setup main_gc for both point and line drawing
2135 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2136 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 ) );
2140 if ( drawpoints && ! draw_track_outline )
2145 * The concept of drawing stops is that a trackpoint
2146 * that is if the next trackpoint has a timestamp far into
2147 * the future, we draw a circle of 6x trackpoint size,
2148 * instead of a rectangle of 2x trackpoint size.
2149 * This is drawn first so the trackpoint will be drawn on top
2152 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2153 /* Stop point. Draw 6x circle. Always in redish colour */
2154 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 );
2156 /* Regular point - draw 2x square. */
2157 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2160 /* Final point - draw 4x circle. */
2161 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 );
2164 if ((!tp->newsegment) && (dp->vtl->drawlines))
2167 /* UTM only: zone check */
2168 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2169 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2172 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2174 if ( draw_track_outline ) {
2175 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2179 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2181 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2183 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2188 tmp[1].y = oldy-FIXALTITUDE(list->data);
2190 tmp[2].y = y-FIXALTITUDE(list->next->data);
2195 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2196 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2198 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2199 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2201 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2206 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2207 // Draw an arrow at the mid point to show the direction of the track
2208 // Code is a rework from vikwindow::draw_ruler()
2209 gint midx = (oldx + x) / 2;
2210 gint midy = (oldy + y) / 2;
2212 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2213 // Avoid divide by zero and ensure at least 1 pixel big
2215 gdouble dx = (oldx - midx) / len;
2216 gdouble dy = (oldy - midy) / len;
2217 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2218 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2228 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2230 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2231 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2233 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2235 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2236 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 ));
2240 * If points are the same in display coordinates, don't draw.
2242 if ( x != oldx || y != oldy )
2244 if ( draw_track_outline )
2245 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2247 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2253 * If points are the same in display coordinates, don't draw.
2255 if ( x != oldx && y != oldy )
2257 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2258 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2266 // Labels drawn after the trackpoints, so the labels are on top
2267 if ( dp->vtl->track_draw_labels ) {
2268 if ( track->max_number_dist_labels > 0 ) {
2269 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2272 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2273 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2279 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2281 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2282 trw_layer_draw_track ( id, track, dp, FALSE );
2286 static void cached_pixbuf_free ( CachedPixbuf *cp )
2288 g_object_unref ( G_OBJECT(cp->pixbuf) );
2289 g_free ( cp->image );
2292 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2294 return strcmp ( cp->image, name );
2297 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2300 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2301 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2302 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2305 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2307 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2309 if ( wp->image && dp->vtl->drawimages )
2311 GdkPixbuf *pixbuf = NULL;
2314 if ( dp->vtl->image_alpha == 0)
2317 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2319 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2322 gchar *image = wp->image;
2323 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2324 if ( ! regularthumb )
2326 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2327 image = "\x12\x00"; /* this shouldn't occur naturally. */
2331 CachedPixbuf *cp = NULL;
2332 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2333 if ( dp->vtl->image_size == 128 )
2334 cp->pixbuf = regularthumb;
2337 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2338 g_assert ( cp->pixbuf );
2339 g_object_unref ( G_OBJECT(regularthumb) );
2341 cp->image = g_strdup ( image );
2343 /* needed so 'click picture' tool knows how big the pic is; we don't
2344 * store it in cp because they may have been freed already. */
2345 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2346 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2348 g_queue_push_head ( dp->vtl->image_cache, cp );
2349 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2350 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2352 pixbuf = cp->pixbuf;
2356 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2362 w = gdk_pixbuf_get_width ( pixbuf );
2363 h = gdk_pixbuf_get_height ( pixbuf );
2365 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2367 if ( dp->highlight ) {
2368 // Highlighted - so draw a little border around the chosen one
2369 // single line seems a little weak so draw 2 of them
2370 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2371 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2372 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2373 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2376 if ( dp->vtl->image_alpha == 255 )
2377 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2379 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2381 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2385 // Draw appropriate symbol - either symbol image or simple types
2386 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2387 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 );
2389 else if ( wp == dp->vtl->current_wp ) {
2390 switch ( dp->vtl->wp_symbol ) {
2391 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;
2392 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;
2393 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;
2394 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 );
2395 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 );
2400 switch ( dp->vtl->wp_symbol ) {
2401 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;
2402 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;
2403 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;
2404 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 );
2405 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;
2410 if ( dp->vtl->drawlabels )
2412 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2413 gint label_x, label_y;
2415 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2417 // Could this stored in the waypoint rather than recreating each pass?
2418 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2420 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2421 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2423 // Fallback if parse failure
2424 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2426 g_free ( wp_label_markup );
2428 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2429 label_x = x - width/2;
2430 if ( wp->symbol_pixbuf )
2431 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2433 label_y = y - dp->vtl->wp_size - height - 2;
2435 /* if highlight mode on, then draw background text in highlight colour */
2436 if ( dp->highlight )
2437 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2439 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2440 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2445 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2447 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2448 trw_layer_draw_waypoint ( id, wp, dp );
2452 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2454 static struct DrawingParams dp;
2455 g_assert ( l != NULL );
2457 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2459 if ( l->tracks_visible )
2460 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2462 if ( l->routes_visible )
2463 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2465 if (l->waypoints_visible)
2466 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2469 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2471 // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2472 // This may seem slightly inefficient to test each time for every layer
2473 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2474 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2475 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2477 trw_layer_draw_with_highlight ( l, data, FALSE );
2480 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2482 // Check the layer for visibility (including all the parents visibilities)
2483 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2485 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2489 * vik_trw_layer_draw_highlight_item:
2491 * Only handles a single track or waypoint ATM
2492 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2494 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2496 // Check the layer for visibility (including all the parents visibilities)
2497 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2500 static struct DrawingParams dp;
2501 init_drawing_params ( &dp, vtl, vvp, TRUE );
2504 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2506 trw_layer_draw_track_cb ( NULL, trk, &dp );
2508 if ( vtl->waypoints_visible && wpt ) {
2509 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2514 * vik_trw_layer_draw_highlight_item:
2516 * Generally for drawing all tracks or routes or waypoints
2517 * trks may be actually routes
2518 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2520 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2522 // Check the layer for visibility (including all the parents visibilities)
2523 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2526 static struct DrawingParams dp;
2527 init_drawing_params ( &dp, vtl, vvp, TRUE );
2530 gboolean is_routes = (trks == vtl->routes);
2531 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2533 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2536 if ( vtl->waypoints_visible && wpts )
2537 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2540 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2543 if ( vtl->track_bg_gc )
2545 g_object_unref ( vtl->track_bg_gc );
2546 vtl->track_bg_gc = NULL;
2548 if ( vtl->track_1color_gc )
2550 g_object_unref ( vtl->track_1color_gc );
2551 vtl->track_1color_gc = NULL;
2553 if ( vtl->current_track_gc )
2555 g_object_unref ( vtl->current_track_gc );
2556 vtl->current_track_gc = NULL;
2558 if ( vtl->current_track_newpoint_gc )
2560 g_object_unref ( vtl->current_track_newpoint_gc );
2561 vtl->current_track_newpoint_gc = NULL;
2564 if ( ! vtl->track_gc )
2566 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2567 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2568 g_array_free ( vtl->track_gc, TRUE );
2569 vtl->track_gc = NULL;
2572 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2574 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2575 gint width = vtl->line_thickness;
2577 if ( vtl->track_gc )
2578 trw_layer_free_track_gcs ( vtl );
2580 if ( vtl->track_bg_gc )
2581 g_object_unref ( vtl->track_bg_gc );
2582 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2584 // Ensure new track drawing heeds line thickness setting
2585 // however always have a minium of 2, as 1 pixel is really narrow
2586 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2588 if ( vtl->current_track_gc )
2589 g_object_unref ( vtl->current_track_gc );
2590 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2591 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2593 // 'newpoint' gc is exactly the same as the current track gc
2594 if ( vtl->current_track_newpoint_gc )
2595 g_object_unref ( vtl->current_track_newpoint_gc );
2596 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2597 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2599 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2601 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2602 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2604 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2605 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2606 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2608 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2610 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2613 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2615 VikTrwLayer *rv = trw_layer_new1 ( vp );
2616 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2618 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2619 /* early exit, as the rest is GUI related */
2623 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2624 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2626 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2627 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2629 trw_layer_new_track_gcs ( rv, vp );
2631 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2632 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2633 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2634 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2636 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2638 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2643 #define SMALL_ICON_SIZE 18
2645 * Can accept a null symbol, and may return null value
2647 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2649 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2650 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2651 // So needing a small icon for the treeview may need some resizing:
2652 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2653 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2657 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2659 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2661 GdkPixbuf *pixbuf = NULL;
2663 if ( track->has_color ) {
2664 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2665 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2666 // Here is some magic found to do the conversion
2667 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2668 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2669 ((track->color.green & 0xff00) << 8) |
2670 (track->color.blue & 0xff00);
2672 gdk_pixbuf_fill ( pixbuf, pixel );
2675 time_t timestamp = 0;
2676 VikTrackpoint *tpt = vik_track_get_tp_first(track);
2677 if ( tpt && tpt->has_timestamp )
2678 timestamp = tpt->timestamp;
2680 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, timestamp );
2683 g_object_unref (pixbuf);
2685 *new_iter = *((GtkTreeIter *) pass_along[1]);
2686 if ( track->is_route )
2687 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2689 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2691 if ( ! track->visible )
2692 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2695 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2697 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2699 time_t timestamp = 0;
2700 if ( wp->has_timestamp )
2701 timestamp = wp->timestamp;
2703 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, timestamp );
2705 *new_iter = *((GtkTreeIter *) pass_along[1]);
2706 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2708 if ( ! wp->visible )
2709 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2712 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2714 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 );
2717 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2719 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 );
2722 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2724 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 );
2727 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2730 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2732 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2733 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2735 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2737 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2740 if ( g_hash_table_size (vtl->routes) > 0 ) {
2741 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2743 pass_along[0] = &(vtl->routes_iter);
2744 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2746 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2748 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2751 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2752 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2754 pass_along[0] = &(vtl->waypoints_iter);
2755 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2757 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2759 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2764 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2768 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2769 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2770 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2771 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2773 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2775 return (t->visible ^= 1);
2779 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2781 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2783 return (t->visible ^= 1);
2787 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2789 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2791 return (t->visible ^= 1);
2801 * Return a property about tracks for this layer
2803 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2805 return vtl->line_thickness;
2808 // Structure to hold multiple track information for a layer
2817 * Build up layer multiple track information via updating the tooltip_tracks structure
2819 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2821 tt->length = tt->length + vik_track_get_length (tr);
2823 // Ensure times are available
2824 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2825 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2826 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2827 if ( trkpt_last->has_timestamp ) {
2829 t1 = vik_track_get_tp_first(tr)->timestamp;
2830 t2 = trkpt_last->timestamp;
2832 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2833 // Hence initialize to the first 'proper' value
2834 if ( tt->start_time == 0 )
2835 tt->start_time = t1;
2836 if ( tt->end_time == 0 )
2839 // Update find the earliest / last times
2840 if ( t1 < tt->start_time )
2841 tt->start_time = t1;
2842 if ( t2 > tt->end_time )
2845 // Keep track of total time
2846 // there maybe gaps within a track (eg segments)
2847 // but this should be generally good enough for a simple indicator
2848 tt->duration = tt->duration + (int)(t2-t1);
2854 * Generate tooltip text for the layer.
2855 * This is relatively complicated as it considers information for
2856 * no tracks, a single track or multiple tracks
2857 * (which may or may not have timing information)
2859 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2870 static gchar tmp_buf[128];
2873 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2875 // Safety check - I think these should always be valid
2876 if ( vtl->tracks && vtl->waypoints ) {
2877 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2878 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2880 GDate* gdate_start = g_date_new ();
2881 g_date_set_time_t (gdate_start, tt.start_time);
2883 GDate* gdate_end = g_date_new ();
2884 g_date_set_time_t (gdate_end, tt.end_time);
2886 if ( g_date_compare (gdate_start, gdate_end) ) {
2887 // Dates differ so print range on separate line
2888 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2889 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2890 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2893 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2894 if ( tt.start_time != 0 )
2895 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2899 if ( tt.length > 0.0 ) {
2900 gdouble len_in_units;
2902 // Setup info dependent on distance units
2903 switch ( a_vik_get_units_distance() ) {
2904 case VIK_UNITS_DISTANCE_MILES:
2905 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2906 len_in_units = VIK_METERS_TO_MILES(tt.length);
2908 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2909 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2910 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2913 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2914 len_in_units = tt.length/1000.0;
2918 // Timing information if available
2920 if ( tt.duration > 0 ) {
2921 g_snprintf (tbuf1, sizeof(tbuf1),
2922 _(" in %d:%02d hrs:mins"),
2923 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2925 g_snprintf (tbuf2, sizeof(tbuf2),
2926 _("\n%sTotal Length %.1f %s%s"),
2927 tbuf3, len_in_units, tbuf4, tbuf1);
2930 // Put together all the elements to form compact tooltip text
2931 g_snprintf (tmp_buf, sizeof(tmp_buf),
2932 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2933 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2935 g_date_free (gdate_start);
2936 g_date_free (gdate_end);
2943 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2947 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2949 // Very simple tooltip - may expand detail in the future...
2950 static gchar tmp_buf[32];
2951 g_snprintf (tmp_buf, sizeof(tmp_buf),
2953 g_hash_table_size (l->tracks));
2957 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2959 // Very simple tooltip - may expand detail in the future...
2960 static gchar tmp_buf[32];
2961 g_snprintf (tmp_buf, sizeof(tmp_buf),
2963 g_hash_table_size (l->routes));
2968 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2969 // Same tooltip for a route
2970 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2973 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2974 tr = g_hash_table_lookup ( l->tracks, sublayer );
2976 tr = g_hash_table_lookup ( l->routes, sublayer );
2979 // Could be a better way of handling strings - but this works...
2980 gchar time_buf1[20];
2981 gchar time_buf2[20];
2982 time_buf1[0] = '\0';
2983 time_buf2[0] = '\0';
2984 static gchar tmp_buf[100];
2985 // Compact info: Short date eg (11/20/99), duration and length
2986 // Hopefully these are the things that are most useful and so promoted into the tooltip
2987 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2988 // %x The preferred date representation for the current locale without the time.
2989 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2990 time_t dur = vik_track_get_duration ( tr );
2992 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2994 // Get length and consider the appropriate distance units
2995 gdouble tr_len = vik_track_get_length(tr);
2996 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2997 switch (dist_units) {
2998 case VIK_UNITS_DISTANCE_KILOMETRES:
2999 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
3001 case VIK_UNITS_DISTANCE_MILES:
3002 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
3004 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3005 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
3014 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3016 // Very simple tooltip - may expand detail in the future...
3017 static gchar tmp_buf[32];
3018 g_snprintf (tmp_buf, sizeof(tmp_buf),
3020 g_hash_table_size (l->waypoints));
3024 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3026 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
3027 // NB It's OK to return NULL
3032 return w->description;
3041 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
3044 * set_statusbar_msg_info_trkpt:
3046 * Function to show track point information on the statusbar
3047 * Items displayed is controlled by the settings format code
3049 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
3051 gchar *statusbar_format_code = NULL;
3052 gboolean need2free = FALSE;
3053 VikTrackpoint *trkpt_prev = NULL;
3054 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3055 // Otherwise use default
3056 statusbar_format_code = g_strdup ( "KEATDN" );
3060 // Format code may want to show speed - so may need previous trkpt to work it out
3061 trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
3064 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
3065 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3069 g_free ( statusbar_format_code );
3073 * Function to show basic waypoint information on the statusbar
3075 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3078 switch (a_vik_get_units_height ()) {
3079 case VIK_UNITS_HEIGHT_FEET:
3080 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3083 //VIK_UNITS_HEIGHT_METRES:
3084 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3088 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3089 // one can easily use the current pointer position to see this if needed
3090 gchar *lat = NULL, *lon = NULL;
3091 static struct LatLon ll;
3092 vik_coord_to_latlon (&(wpt->coord), &ll);
3093 a_coords_latlon_to_string ( &ll, &lat, &lon );
3095 // Combine parts to make overall message
3098 // Add comment if available
3099 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3101 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3102 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3109 * General layer selection function, find out which bit is selected and take appropriate action
3111 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3114 l->current_wp = NULL;
3115 l->current_wp_id = NULL;
3116 trw_layer_cancel_current_tp ( l, FALSE );
3119 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3123 case VIK_TREEVIEW_TYPE_LAYER:
3125 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3126 /* Mark for redraw */
3131 case VIK_TREEVIEW_TYPE_SUBLAYER:
3135 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3137 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3138 /* Mark for redraw */
3142 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3144 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3145 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3146 /* Mark for redraw */
3150 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3152 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3153 /* Mark for redraw */
3157 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3159 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3160 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3161 /* Mark for redraw */
3165 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3167 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3168 /* Mark for redraw */
3172 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3174 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3176 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3177 // Show some waypoint info
3178 set_statusbar_msg_info_wpt ( l, wpt );
3179 /* Mark for redraw */
3186 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3195 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3200 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3205 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3210 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3212 return l->waypoints;
3215 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3217 return vtl->tracks_iters;
3220 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3222 return vtl->routes_iters;
3225 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3227 return vtl->waypoints;
3230 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3232 return ! ( g_hash_table_size ( vtl->tracks ) ||
3233 g_hash_table_size ( vtl->routes ) ||
3234 g_hash_table_size ( vtl->waypoints ) );
3237 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3239 return vtl->tracks_visible;
3242 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3244 return vtl->routes_visible;
3247 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3249 return vtl->waypoints_visible;
3253 * ATM use a case sensitive find
3254 * Finds the first one
3256 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3258 if ( wp && wp->name )
3259 if ( ! strcmp ( wp->name, name ) )
3265 * Get waypoint by name - not guaranteed to be unique
3266 * Finds the first one
3268 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3270 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3274 * ATM use a case sensitive find
3275 * Finds the first one
3277 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3279 if ( trk && trk->name )
3280 if ( ! strcmp ( trk->name, name ) )
3286 * Get track by name - not guaranteed to be unique
3287 * Finds the first one
3289 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3291 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3295 * Get route by name - not guaranteed to be unique
3296 * Finds the first one
3298 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3300 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3303 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3305 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3306 maxmin[0].lat = trk->bbox.north;
3307 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3308 maxmin[1].lat = trk->bbox.south;
3309 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3310 maxmin[0].lon = trk->bbox.east;
3311 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3312 maxmin[1].lon = trk->bbox.west;
3315 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3317 // Continually reuse maxmin to find the latest maximum and minimum values
3318 // First set to waypoints bounds
3319 maxmin[0].lat = vtl->waypoints_bbox.north;
3320 maxmin[1].lat = vtl->waypoints_bbox.south;
3321 maxmin[0].lon = vtl->waypoints_bbox.east;
3322 maxmin[1].lon = vtl->waypoints_bbox.west;
3323 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3324 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3327 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3329 /* 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... */
3330 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3331 trw_layer_find_maxmin (vtl, maxmin);
3332 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3336 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3337 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3342 static void trw_layer_centerize ( menu_array_layer values )
3344 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3346 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3347 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3349 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3352 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3354 /* First set the center [in case previously viewing from elsewhere] */
3355 /* Then loop through zoom levels until provided positions are in view */
3356 /* This method is not particularly fast - but should work well enough */
3357 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3359 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3360 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3362 /* Convert into definite 'smallest' and 'largest' positions */
3363 struct LatLon minmin;
3364 if ( maxmin[0].lat < maxmin[1].lat )
3365 minmin.lat = maxmin[0].lat;
3367 minmin.lat = maxmin[1].lat;
3369 struct LatLon maxmax;
3370 if ( maxmin[0].lon > maxmin[1].lon )
3371 maxmax.lon = maxmin[0].lon;
3373 maxmax.lon = maxmin[1].lon;
3375 /* Never zoom in too far - generally not that useful, as too close ! */
3376 /* Always recalculate the 'best' zoom level */
3378 vik_viewport_set_zoom ( vvp, zoom );
3380 gdouble min_lat, max_lat, min_lon, max_lon;
3381 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3382 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3383 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3384 /* NB I think the logic used in this test to determine if the bounds is within view
3385 fails if track goes across 180 degrees longitude.
3386 Hopefully that situation is not too common...
3387 Mind you viking doesn't really do edge locations to well anyway */
3388 if ( min_lat < minmin.lat &&
3389 max_lat > minmin.lat &&
3390 min_lon < maxmax.lon &&
3391 max_lon > maxmax.lon )
3392 /* Found within zoom level */
3397 vik_viewport_set_zoom ( vvp, zoom );
3401 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3403 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3404 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3405 trw_layer_find_maxmin (vtl, maxmin);
3406 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3409 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3414 static void trw_layer_auto_view ( menu_array_layer values )
3416 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3417 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3418 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3419 vik_layers_panel_emit_update ( vlp );
3422 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3425 static void trw_layer_export_gpspoint ( menu_array_layer values )
3427 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3429 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3431 g_free ( auto_save_name );
3434 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3436 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3438 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3440 g_free ( auto_save_name );
3443 static void trw_layer_export_gpx ( menu_array_layer values )
3445 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3447 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3449 g_free ( auto_save_name );
3452 static void trw_layer_export_kml ( menu_array_layer values )
3454 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3456 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3458 g_free ( auto_save_name );
3461 static void trw_layer_export_geojson ( menu_array_layer values )
3463 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3465 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3467 g_free ( auto_save_name );
3470 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3472 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3473 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3476 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3478 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3481 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3483 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3486 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3488 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3490 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3491 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3493 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3495 if ( !trk || !trk->name )
3498 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3500 gchar *label = NULL;
3501 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3502 label = _("Export Route as GPX");
3504 label = _("Export Track as GPX");
3505 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3507 g_free ( auto_save_name );
3510 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3512 wpu_udata *user_data = udata;
3513 if ( wp == user_data->wp ) {
3514 user_data->uuid = id;
3520 static void trw_layer_goto_wp ( menu_array_layer values )
3522 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3523 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3524 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3525 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3526 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3528 GTK_RESPONSE_REJECT,
3530 GTK_RESPONSE_ACCEPT,
3533 GtkWidget *label, *entry;
3534 label = gtk_label_new(_("Waypoint Name:"));
3535 entry = gtk_entry_new();
3537 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3538 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3539 gtk_widget_show_all ( label );
3540 gtk_widget_show_all ( entry );
3542 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3544 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3546 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3547 // Find *first* wp with the given name
3548 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3551 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3554 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3555 vik_layers_panel_emit_update ( vlp );
3557 // Find and select on the side panel
3562 // Hmmm, want key of it
3563 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3565 if ( wpf && udata.uuid ) {
3566 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3567 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3576 gtk_widget_destroy ( dia );
3579 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3581 gchar *default_name = highest_wp_number_get(vtl);
3582 VikWaypoint *wp = vik_waypoint_new();
3583 gchar *returned_name;
3585 wp->coord = *def_coord;
3587 // Attempt to auto set height if DEM data is available
3588 vik_waypoint_apply_dem_data ( wp, TRUE );
3590 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3592 if ( returned_name )
3595 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3596 g_free (default_name);
3597 g_free (returned_name);
3600 g_free (default_name);
3601 vik_waypoint_free(wp);
3605 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3607 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3608 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3609 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3610 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3611 VikViewport *vvp = vik_window_viewport(vw);
3613 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3614 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3615 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3616 trw_layer_calculate_bounds_waypoints ( vtl );
3617 vik_layers_panel_emit_update ( vlp );
3620 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3622 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3623 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3624 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3626 trw_layer_find_maxmin (vtl, maxmin);
3627 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3628 trw_layer_calculate_bounds_waypoints ( vtl );
3629 vik_layers_panel_emit_update ( vlp );
3632 #ifdef VIK_CONFIG_GEOTAG
3633 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3635 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3637 // Update directly - not changing the mtime
3638 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3641 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3643 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3646 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3650 * Use code in separate file for this feature as reasonably complex
3652 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3654 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3655 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3656 // Unset so can be reverified later if necessary
3657 vtl->has_verified_thumbnails = FALSE;
3659 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3665 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3667 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3668 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3670 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3676 static void trw_layer_geotagging ( menu_array_layer values )
3678 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3679 // Unset so can be reverified later if necessary
3680 vtl->has_verified_thumbnails = FALSE;
3682 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3689 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3691 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3693 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3694 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3695 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3696 VikViewport *vvp = vik_window_viewport(vw);
3698 vik_datasource_mode_t mode = datasource->mode;
3699 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3700 mode = VIK_DATASOURCE_ADDTOLAYER;
3701 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3705 * Acquire into this TRW Layer straight from GPS Device
3707 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3709 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3713 * Acquire into this TRW Layer from Directions
3715 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3717 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3721 * Acquire into this TRW Layer from an entered URL
3723 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3725 trw_layer_acquire ( values, &vik_datasource_url_interface );
3728 #ifdef VIK_CONFIG_OPENSTREETMAP
3730 * Acquire into this TRW Layer from OSM
3732 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3734 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3738 * Acquire into this TRW Layer from OSM for 'My' Traces
3740 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3742 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3746 #ifdef VIK_CONFIG_GEOCACHES
3748 * Acquire into this TRW Layer from Geocaching.com
3750 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3752 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3756 #ifdef VIK_CONFIG_GEOTAG
3758 * Acquire into this TRW Layer from images
3760 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3762 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3764 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3766 // Reverify thumbnails as they may have changed
3767 vtl->has_verified_thumbnails = FALSE;
3768 trw_layer_verify_thumbnails ( vtl, NULL );
3773 * Acquire into this TRW Layer from any GPS Babel supported file
3775 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3777 trw_layer_acquire ( values, &vik_datasource_file_interface );
3780 static void trw_layer_gps_upload ( menu_array_layer values )
3782 menu_array_sublayer data;
3784 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3786 data[MA_VTL] = values[MA_VTL];
3787 data[MA_VLP] = values[MA_VLP];
3789 trw_layer_gps_upload_any ( data );
3793 * If pass_along[3] is defined that this will upload just that track
3795 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3797 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3798 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3800 // May not actually get a track here as values[2&3] can be null
3801 VikTrack *track = NULL;
3802 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3803 gboolean xfer_all = FALSE;
3805 if ( values[MA_SUBTYPE] ) {
3807 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3808 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3811 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3812 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3815 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3818 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3822 else if ( !values[MA_CONFIRM] )
3823 xfer_all = TRUE; // i.e. whole layer
3825 if (track && !track->visible) {
3826 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3830 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3831 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3832 GTK_DIALOG_DESTROY_WITH_PARENT,
3834 GTK_RESPONSE_ACCEPT,
3836 GTK_RESPONSE_REJECT,
3839 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3840 GtkWidget *response_w = NULL;
3841 #if GTK_CHECK_VERSION (2, 20, 0)
3842 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3846 gtk_widget_grab_focus ( response_w );
3848 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3850 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3851 datasource_gps_clean_up ( dgs );
3852 gtk_widget_destroy ( dialog );
3856 // Get info from reused datasource dialog widgets
3857 gchar* protocol = datasource_gps_get_protocol ( dgs );
3858 gchar* port = datasource_gps_get_descriptor ( dgs );
3859 // NB don't free the above strings as they're references to values held elsewhere
3860 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3861 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3862 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3863 gboolean turn_off = datasource_gps_get_off ( dgs );
3865 gtk_widget_destroy ( dialog );
3867 // When called from the viewport - work the corresponding layerspanel:
3869 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3872 // Apply settings to transfer to the GPS device
3879 vik_layers_panel_get_viewport (vlp),
3887 static void trw_layer_new_wp ( menu_array_layer values )
3889 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3890 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3891 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3892 instead return true if you want to update. */
3893 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 ) {
3894 trw_layer_calculate_bounds_waypoints ( vtl );
3895 vik_layers_panel_emit_update ( vlp );
3899 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3901 vtl->current_track = vik_track_new();
3902 vik_track_set_defaults ( vtl->current_track );
3903 vtl->current_track->visible = TRUE;
3904 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3905 // Create track with the preferred colour from the layer properties
3906 vtl->current_track->color = vtl->track_color;
3908 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3909 vtl->current_track->has_color = TRUE;
3910 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3913 static void trw_layer_new_track ( menu_array_layer values )
3915 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3917 if ( ! vtl->current_track ) {
3918 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3919 new_track_create_common ( vtl, name );
3922 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3926 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3928 vtl->current_track = vik_track_new();
3929 vik_track_set_defaults ( vtl->current_track );
3930 vtl->current_track->visible = TRUE;
3931 vtl->current_track->is_route = TRUE;
3932 // By default make all routes red
3933 vtl->current_track->has_color = TRUE;
3934 gdk_color_parse ( "red", &vtl->current_track->color );
3935 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3938 static void trw_layer_new_route ( menu_array_layer values )
3940 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3942 if ( ! vtl->current_track ) {
3943 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3944 new_route_create_common ( vtl, name );
3946 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3950 static void trw_layer_auto_routes_view ( menu_array_layer values )
3952 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3953 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3955 if ( g_hash_table_size (vtl->routes) > 0 ) {
3956 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3957 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3958 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3959 vik_layers_panel_emit_update ( vlp );
3964 static void trw_layer_finish_track ( menu_array_layer values )
3966 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3967 vtl->current_track = NULL;
3968 vtl->route_finder_started = FALSE;
3969 vik_layer_emit_update ( VIK_LAYER(vtl) );
3972 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3974 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3975 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3977 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3978 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3979 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3980 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3981 vik_layers_panel_emit_update ( vlp );
3985 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3987 /* NB do not care if wp is visible or not */
3988 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3991 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3993 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3994 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3996 /* Only 1 waypoint - jump straight to it */
3997 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3998 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3999 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
4001 /* If at least 2 waypoints - find center and then zoom to fit */
4002 else if ( g_hash_table_size (vtl->waypoints) > 1 )
4004 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4005 maxmin[0].lat = vtl->waypoints_bbox.north;
4006 maxmin[1].lat = vtl->waypoints_bbox.south;
4007 maxmin[0].lon = vtl->waypoints_bbox.east;
4008 maxmin[1].lon = vtl->waypoints_bbox.west;
4009 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4012 vik_layers_panel_emit_update ( vlp );
4015 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
4017 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
4020 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
4022 if ( values[MA_MISC] ) {
4023 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
4024 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
4028 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
4030 static menu_array_layer pass_along;
4032 GtkWidget *export_submenu;
4033 pass_along[MA_VTL] = vtl;
4034 pass_along[MA_VLP] = vlp;
4036 item = gtk_menu_item_new();
4037 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4038 gtk_widget_show ( item );
4040 if ( vtl->current_track ) {
4041 if ( vtl->current_track->is_route )
4042 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
4044 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
4045 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
4046 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4047 gtk_widget_show ( item );
4050 item = gtk_menu_item_new ();
4051 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4052 gtk_widget_show ( item );
4055 /* Now with icons */
4056 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4057 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4058 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4059 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4060 gtk_widget_show ( item );
4062 GtkWidget *view_submenu = gtk_menu_new();
4063 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4064 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4065 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4066 gtk_widget_show ( item );
4067 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4069 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4070 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4071 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4072 gtk_widget_show ( item );
4074 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4076 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4077 gtk_widget_show ( item );
4079 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4081 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4082 gtk_widget_show ( item );
4084 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4087 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4088 gtk_widget_show ( item );
4090 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4092 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4093 gtk_widget_show ( item );
4095 export_submenu = gtk_menu_new ();
4096 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4098 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4099 gtk_widget_show ( item );
4100 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4102 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4104 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4105 gtk_widget_show ( item );
4107 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4108 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4109 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4110 gtk_widget_show ( item );
4112 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4114 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4115 gtk_widget_show ( item );
4117 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4119 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4120 gtk_widget_show ( item );
4122 if ( have_geojson_export ) {
4123 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4125 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4126 gtk_widget_show ( item );
4129 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4131 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4132 gtk_widget_show ( item );
4134 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4135 item = gtk_menu_item_new_with_mnemonic ( external1 );
4136 g_free ( external1 );
4137 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4138 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4139 gtk_widget_show ( item );
4141 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4142 item = gtk_menu_item_new_with_mnemonic ( external2 );
4143 g_free ( external2 );
4144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4145 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4146 gtk_widget_show ( item );
4148 GtkWidget *new_submenu = gtk_menu_new();
4149 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4150 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4151 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4152 gtk_widget_show(item);
4153 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4155 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4156 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4157 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4158 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4159 gtk_widget_show ( item );
4161 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4162 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4164 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4165 gtk_widget_show ( item );
4166 // Make it available only when a new track *not* already in progress
4167 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4169 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4170 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4172 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4173 gtk_widget_show ( item );
4174 // Make it available only when a new track *not* already in progress
4175 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4177 #ifdef VIK_CONFIG_GEOTAG
4178 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4179 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4180 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4181 gtk_widget_show ( item );
4184 GtkWidget *acquire_submenu = gtk_menu_new ();
4185 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4186 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4187 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4188 gtk_widget_show ( item );
4189 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4191 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4192 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4193 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4194 gtk_widget_show ( item );
4196 /* FIXME: only add menu when at least a routing engine has support for Directions */
4197 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4198 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4199 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4200 gtk_widget_show ( item );
4202 #ifdef VIK_CONFIG_OPENSTREETMAP
4203 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4205 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4206 gtk_widget_show ( item );
4208 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4210 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4211 gtk_widget_show ( item );
4214 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4216 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4217 gtk_widget_show ( item );
4219 #ifdef VIK_CONFIG_GEONAMES
4220 GtkWidget *wikipedia_submenu = gtk_menu_new();
4221 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4222 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4223 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4224 gtk_widget_show(item);
4225 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4227 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4228 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4229 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4230 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4231 gtk_widget_show ( item );
4233 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4234 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4235 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4236 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4237 gtk_widget_show ( item );
4240 #ifdef VIK_CONFIG_GEOCACHES
4241 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4243 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4244 gtk_widget_show ( item );
4247 #ifdef VIK_CONFIG_GEOTAG
4248 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4250 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4251 gtk_widget_show ( item );
4254 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4255 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4256 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4257 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4258 gtk_widget_show ( item );
4260 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4262 GtkWidget *upload_submenu = gtk_menu_new ();
4263 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4264 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4265 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4266 gtk_widget_show ( item );
4267 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4269 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4270 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4271 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4272 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4273 gtk_widget_show ( item );
4275 #ifdef VIK_CONFIG_OPENSTREETMAP
4276 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4277 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4278 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4279 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4280 gtk_widget_show ( item );
4283 GtkWidget *delete_submenu = gtk_menu_new ();
4284 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4286 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4287 gtk_widget_show ( item );
4288 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4290 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4291 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4292 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4293 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4294 gtk_widget_show ( item );
4296 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4298 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4299 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4300 gtk_widget_show ( item );
4302 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4303 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4305 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4306 gtk_widget_show ( item );
4308 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4309 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4311 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4312 gtk_widget_show ( item );
4314 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4315 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4317 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4318 gtk_widget_show ( item );
4320 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4321 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4323 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4324 gtk_widget_show ( item );
4326 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4327 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4329 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4330 gtk_widget_show ( item );
4333 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4334 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4336 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4337 gtk_widget_show ( item );
4340 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4341 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4343 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4344 gtk_widget_show ( item );
4345 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4347 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4348 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4350 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4351 gtk_widget_show ( item );
4352 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4355 // Fake Waypoint UUIDs vi simple increasing integer
4356 static guint wp_uuid = 0;
4358 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4362 vik_waypoint_set_name (wp, name);
4364 if ( VIK_LAYER(vtl)->realized )
4366 // Do we need to create the sublayer:
4367 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4368 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4371 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4373 time_t timestamp = 0;
4374 if ( wp->has_timestamp )
4375 timestamp = wp->timestamp;
4377 // Visibility column always needed for waypoints
4378 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, timestamp );
4380 // Actual setting of visibility dependent on the waypoint
4381 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4383 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4385 // Sort now as post_read is not called on a realized waypoint
4386 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4389 highest_wp_number_add_wp(vtl, name);
4390 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4394 // Fake Track UUIDs vi simple increasing integer
4395 static guint tr_uuid = 0;
4397 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4401 vik_track_set_name (t, name);
4403 if ( VIK_LAYER(vtl)->realized )
4405 // Do we need to create the sublayer:
4406 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4407 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4410 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4412 time_t timestamp = 0;
4413 VikTrackpoint *tpt = vik_track_get_tp_first(t);
4414 if ( tpt && tpt->has_timestamp )
4415 timestamp = tpt->timestamp;
4417 // Visibility column always needed for tracks
4418 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, timestamp );
4420 // Actual setting of visibility dependent on the track
4421 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4423 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4425 // Sort now as post_read is not called on a realized track
4426 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4429 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4431 trw_layer_update_treeview ( vtl, t );
4434 // Fake Route UUIDs vi simple increasing integer
4435 static guint rt_uuid = 0;
4437 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4441 vik_track_set_name (t, name);
4443 if ( VIK_LAYER(vtl)->realized )
4445 // Do we need to create the sublayer:
4446 if ( g_hash_table_size (vtl->routes) == 0 ) {
4447 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4450 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4451 // Visibility column always needed for routes
4452 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, 0 ); // Routes don't have times
4453 // Actual setting of visibility dependent on the route
4454 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4456 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4458 // Sort now as post_read is not called on a realized route
4459 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4462 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4464 trw_layer_update_treeview ( vtl, t );
4467 /* to be called whenever a track has been deleted or may have been changed. */
4468 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4470 if (vtl->current_tp_track == trk )
4471 trw_layer_cancel_current_tp ( vtl, FALSE );
4475 * Normally this is done to due the waypoint size preference having changed
4477 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4479 GHashTableIter iter;
4480 gpointer key, value;
4483 g_hash_table_iter_init ( &iter, vtl->waypoints );
4484 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4485 VikWaypoint *wp = VIK_WAYPOINT(value);
4487 // Reapply symbol setting to update the pixbuf
4488 gchar *tmp_symbol = g_strdup ( wp->symbol );
4489 vik_waypoint_set_symbol ( wp, tmp_symbol );
4490 g_free ( tmp_symbol );
4496 * trw_layer_new_unique_sublayer_name:
4498 * Allocates a unique new name
4500 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4503 gchar *newname = g_strdup(name);
4508 switch ( sublayer_type ) {
4509 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4510 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4512 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4513 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4516 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4519 // If found a name already in use try adding 1 to it and we try again
4521 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4523 newname = new_newname;
4526 } while ( id != NULL);
4531 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4533 // No more uniqueness of name forced when loading from a file
4534 // This now makes this function a little redunant as we just flow the parameters through
4535 vik_trw_layer_add_waypoint ( vtl, name, wp );
4538 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4540 if ( vtl->route_finder_append && vtl->current_track ) {
4541 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4543 // enforce end of current track equal to start of tr
4544 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4545 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4546 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4547 vik_track_add_trackpoint ( vtl->current_track,
4548 vik_trackpoint_copy ( cur_end ),
4552 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4553 vik_track_free ( tr );
4554 vtl->route_finder_append = FALSE; /* this means we have added it */
4557 // No more uniqueness of name forced when loading from a file
4559 vik_trw_layer_add_route ( vtl, name, tr );
4561 vik_trw_layer_add_track ( vtl, name, tr );
4563 if ( vtl->route_finder_check_added_track ) {
4564 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4565 vtl->route_finder_added_track = tr;
4570 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4572 *l = g_list_append(*l, id);
4576 * Move an item from one TRW layer to another TRW layer
4578 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4580 // TODO reconsider strategy when moving within layer (if anything...)
4581 gboolean rename = ( vtl_src != vtl_dest );
4585 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4586 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4590 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4592 newname = g_strdup ( trk->name );
4594 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4595 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4597 vik_trw_layer_delete_track ( vtl_src, trk );
4598 // Reset layer timestamps in case they have now changed
4599 vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4600 vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4603 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4604 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4608 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4610 newname = g_strdup ( trk->name );
4612 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4613 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4615 vik_trw_layer_delete_route ( vtl_src, trk );
4618 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4619 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4623 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4625 newname = g_strdup ( wp->name );
4627 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4628 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4630 trw_layer_delete_waypoint ( vtl_src, wp );
4632 // Recalculate bounds even if not renamed as maybe dragged between layers
4633 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4634 trw_layer_calculate_bounds_waypoints ( vtl_src );
4635 // Reset layer timestamps in case they have now changed
4636 vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4637 vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4641 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4643 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4644 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4646 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4647 GList *items = NULL;
4650 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4651 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4653 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4654 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4656 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4657 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4662 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4663 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4664 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4665 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4667 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4674 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4675 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4679 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4681 trku_udata *user_data = udata;
4682 if ( trk == user_data->trk ) {
4683 user_data->uuid = id;
4689 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4691 gboolean was_visible = FALSE;
4692 if ( trk && trk->name ) {
4694 if ( trk == vtl->current_track ) {
4695 vtl->current_track = NULL;
4696 vtl->current_tp_track = NULL;
4697 vtl->current_tp_id = NULL;
4698 vtl->moving_tp = FALSE;
4699 vtl->route_finder_started = FALSE;
4702 was_visible = trk->visible;
4704 if ( trk == vtl->route_finder_added_track )
4705 vtl->route_finder_added_track = NULL;
4711 // Hmmm, want key of it
4712 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4714 if ( trkf && udata.uuid ) {
4715 /* could be current_tp, so we have to check */
4716 trw_layer_cancel_tps_of_track ( vtl, trk );
4718 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4721 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4722 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4723 g_hash_table_remove ( vtl->tracks, udata.uuid );
4725 // If last sublayer, then remove sublayer container
4726 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4727 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4730 // Incase it was selected (no item delete signal ATM)
4731 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4737 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4739 gboolean was_visible = FALSE;
4741 if ( trk && trk->name ) {
4743 if ( trk == vtl->current_track ) {
4744 vtl->current_track = NULL;
4745 vtl->current_tp_track = NULL;
4746 vtl->current_tp_id = NULL;
4747 vtl->moving_tp = FALSE;
4750 was_visible = trk->visible;
4752 if ( trk == vtl->route_finder_added_track )
4753 vtl->route_finder_added_track = NULL;
4759 // Hmmm, want key of it
4760 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4762 if ( trkf && udata.uuid ) {
4763 /* could be current_tp, so we have to check */
4764 trw_layer_cancel_tps_of_track ( vtl, trk );
4766 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4769 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4770 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4771 g_hash_table_remove ( vtl->routes, udata.uuid );
4773 // If last sublayer, then remove sublayer container
4774 if ( g_hash_table_size (vtl->routes) == 0 ) {
4775 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4778 // Incase it was selected (no item delete signal ATM)
4779 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4785 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4787 gboolean was_visible = FALSE;
4789 if ( wp && wp->name ) {
4791 if ( wp == vtl->current_wp ) {
4792 vtl->current_wp = NULL;
4793 vtl->current_wp_id = NULL;
4794 vtl->moving_wp = FALSE;
4797 was_visible = wp->visible;
4803 // Hmmm, want key of it
4804 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4806 if ( wpf && udata.uuid ) {
4807 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4810 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4811 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4813 highest_wp_number_remove_wp(vtl, wp->name);
4814 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4816 // If last sublayer, then remove sublayer container
4817 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4818 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4821 // Incase it was selected (no item delete signal ATM)
4822 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4830 // Only for temporary use by trw_layer_delete_waypoint_by_name
4831 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4833 wpu_udata *user_data = udata;
4834 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4835 user_data->uuid = id;
4842 * Delete a waypoint by the given name
4843 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4844 * as there be multiple waypoints with the same name
4846 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4849 // Fake a waypoint with the given name
4850 udata.wp = vik_waypoint_new ();
4851 vik_waypoint_set_name (udata.wp, name);
4852 // Currently only the name is used in this waypoint find function
4855 // Hmmm, want key of it
4856 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4858 vik_waypoint_free (udata.wp);
4860 if ( wpf && udata.uuid )
4861 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4867 VikTrack *trk; // input
4868 gpointer uuid; // output
4871 // Only for temporary use by trw_layer_delete_track_by_name
4872 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4874 tpu_udata *user_data = udata;
4875 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4876 user_data->uuid = id;
4883 * Delete a track by the given name
4884 * NOTE: ATM this will delete the first encountered Track with the specified name
4885 * as there may be multiple tracks with the same name within the specified hash table
4887 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4890 // Fake a track with the given name
4891 udata.trk = vik_track_new ();
4892 vik_track_set_name (udata.trk, name);
4893 // Currently only the name is used in this waypoint find function
4896 // Hmmm, want key of it
4897 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4899 vik_track_free (udata.trk);
4901 if ( trkf && udata.uuid ) {
4902 // This could be a little better written...
4903 if ( vtl->tracks == ht_tracks )
4904 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4905 if ( vtl->routes == ht_tracks )
4906 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4913 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4915 vik_treeview_item_delete (vt, it );
4918 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4921 vtl->current_track = NULL;
4922 vtl->route_finder_added_track = NULL;
4923 if (vtl->current_tp_track)
4924 trw_layer_cancel_current_tp(vtl, FALSE);
4926 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4927 g_hash_table_remove_all(vtl->routes_iters);
4928 g_hash_table_remove_all(vtl->routes);
4930 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4932 vik_layer_emit_update ( VIK_LAYER(vtl) );
4935 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4938 vtl->current_track = NULL;
4939 vtl->route_finder_added_track = NULL;
4940 if (vtl->current_tp_track)
4941 trw_layer_cancel_current_tp(vtl, FALSE);
4943 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4944 g_hash_table_remove_all(vtl->tracks_iters);
4945 g_hash_table_remove_all(vtl->tracks);
4947 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4949 vik_layer_emit_update ( VIK_LAYER(vtl) );
4952 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4954 vtl->current_wp = NULL;
4955 vtl->current_wp_id = NULL;
4956 vtl->moving_wp = FALSE;
4958 highest_wp_number_reset(vtl);
4960 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4961 g_hash_table_remove_all(vtl->waypoints_iters);
4962 g_hash_table_remove_all(vtl->waypoints);
4964 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4966 vik_layer_emit_update ( VIK_LAYER(vtl) );
4969 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4971 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4972 // Get confirmation from the user
4973 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4974 _("Are you sure you want to delete all tracks in %s?"),
4975 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4976 vik_trw_layer_delete_all_tracks (vtl);
4979 static void trw_layer_delete_all_routes ( menu_array_layer values )
4981 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4982 // Get confirmation from the user
4983 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4984 _("Are you sure you want to delete all routes in %s?"),
4985 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4986 vik_trw_layer_delete_all_routes (vtl);
4989 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4991 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4992 // Get confirmation from the user
4993 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4994 _("Are you sure you want to delete all waypoints in %s?"),
4995 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4996 vik_trw_layer_delete_all_waypoints (vtl);
4999 static void trw_layer_delete_item ( menu_array_sublayer values )
5001 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5002 gboolean was_visible = FALSE;
5003 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5005 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5006 if ( wp && wp->name ) {
5007 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5008 // Get confirmation from the user
5009 // Maybe this Waypoint Delete should be optional as is it could get annoying...
5010 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5011 _("Are you sure you want to delete the waypoint \"%s\"?"),
5014 was_visible = trw_layer_delete_waypoint ( vtl, wp );
5015 trw_layer_calculate_bounds_waypoints ( vtl );
5016 // Reset layer timestamp in case it has now changed
5017 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5020 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5022 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5023 if ( trk && trk->name ) {
5024 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5025 // Get confirmation from the user
5026 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5027 _("Are you sure you want to delete the track \"%s\"?"),
5030 was_visible = vik_trw_layer_delete_track ( vtl, trk );
5031 // Reset layer timestamp in case it has now changed
5032 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5037 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5038 if ( trk && trk->name ) {
5039 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5040 // Get confirmation from the user
5041 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5042 _("Are you sure you want to delete the route \"%s\"?"),
5045 was_visible = vik_trw_layer_delete_route ( vtl, trk );
5049 vik_layer_emit_update ( VIK_LAYER(vtl) );
5053 * Rename waypoint and maintain corresponding name of waypoint in the treeview
5055 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
5057 vik_waypoint_set_name ( wp, new_name );
5059 // Now update the treeview as well
5064 // Need key of it for treeview update
5065 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5067 if ( wpf && udataU.uuid ) {
5068 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5071 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
5072 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
5078 * Maintain icon of waypoint in the treeview
5080 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5082 // update the treeview
5087 // Need key of it for treeview update
5088 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5090 if ( wpf && udataU.uuid ) {
5091 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5094 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5099 static void trw_layer_properties_item ( menu_array_sublayer values )
5101 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5102 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5104 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5106 if ( wp && wp->name )
5108 gboolean updated = FALSE;
5109 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5111 trw_layer_waypoint_rename ( vtl, wp, new_name );
5113 if ( updated && values[MA_TV_ITER] )
5114 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5116 if ( updated && VIK_LAYER(vtl)->visible )
5117 vik_layer_emit_update ( VIK_LAYER(vtl) );
5123 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5124 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5126 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5128 if ( tr && tr->name )
5130 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5141 * trw_layer_track_statistics:
5143 * Show track statistics.
5144 * ATM jump to the stats page in the properties
5145 * TODO: consider separating the stats into an individual dialog?
5147 static void trw_layer_track_statistics ( menu_array_sublayer values )
5149 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5151 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5152 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5154 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5156 if ( trk && trk->name ) {
5157 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5167 * Update the treeview of the track id - primarily to update the icon
5169 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5175 gpointer *trkf = NULL;
5176 if ( trk->is_route )
5177 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5179 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5181 if ( trkf && udata.uuid ) {
5183 GtkTreeIter *iter = NULL;
5184 if ( trk->is_route )
5185 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5187 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5190 // TODO: Make this a function
5191 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5192 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5193 ((trk->color.green & 0xff00) << 8) |
5194 (trk->color.blue & 0xff00);
5195 gdk_pixbuf_fill ( pixbuf, pixel );
5196 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5197 g_object_unref (pixbuf);
5204 Parameter 1 -> VikLayersPanel
5205 Parameter 2 -> VikLayer
5206 Parameter 3 -> VikViewport
5208 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5211 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5212 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5215 /* since vlp not set, vl & vvp should be valid instead! */
5217 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5218 vik_layer_emit_update ( VIK_LAYER(vl) );
5223 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5225 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5227 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5228 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5230 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5232 if ( track && track->trackpoints )
5233 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5236 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5238 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5240 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5241 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5243 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5245 if ( track && track->trackpoints )
5247 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5249 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5250 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5251 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5252 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5253 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5257 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5259 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5261 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5262 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5264 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5269 // Converting a track to a route can be a bit more complicated,
5270 // so give a chance to change our minds:
5271 if ( !trk->is_route &&
5272 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5273 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5275 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5276 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5281 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5284 trk_copy->is_route = !trk_copy->is_route;
5286 // ATM can't set name to self - so must create temporary copy
5287 gchar *name = g_strdup ( trk_copy->name );
5289 // Delete old one and then add new one
5290 if ( trk->is_route ) {
5291 vik_trw_layer_delete_route ( vtl, trk );
5292 vik_trw_layer_add_track ( vtl, name, trk_copy );
5295 // Extra route conversion bits...
5296 vik_track_merge_segments ( trk_copy );
5297 vik_track_to_routepoints ( trk_copy );
5299 vik_trw_layer_delete_track ( vtl, trk );
5300 vik_trw_layer_add_route ( vtl, name, trk_copy );
5304 // Update in case color of track / route changes when moving between sublayers
5305 vik_layer_emit_update ( VIK_LAYER(vtl) );
5308 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5310 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5312 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5313 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5315 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5318 vik_track_anonymize_times ( track );
5321 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5323 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5325 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5326 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5328 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5333 vtl->current_track = track;
5334 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);
5336 if ( track->trackpoints )
5337 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5341 * extend a track using route finder
5343 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5345 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5346 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5350 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5351 vtl->current_track = track;
5352 vtl->route_finder_started = TRUE;
5354 if ( track->trackpoints )
5355 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5361 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5363 // If have a vlp then perform a basic test to see if any DEM info available...
5365 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5367 if ( !g_list_length(dems) ) {
5368 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5376 * apply_dem_data_common:
5378 * A common function for applying the DEM values and reporting the results.
5380 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5382 if ( !trw_layer_dem_test ( vtl, vlp ) )
5385 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5386 // Inform user how much was changed
5388 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5389 g_snprintf(str, 64, tmp_str, changed);
5390 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5393 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5395 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5397 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5398 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5400 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5403 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5406 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5408 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5410 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5411 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5413 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5416 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5422 * A common function for applying the elevation smoothing and reporting the results.
5424 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5426 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5427 // Inform user how much was changed
5429 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5430 g_snprintf(str, 64, tmp_str, changed);
5431 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5437 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5439 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5441 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5442 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5444 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5449 smooth_it ( vtl, track, FALSE );
5452 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5454 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5456 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5457 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5459 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5464 smooth_it ( vtl, track, TRUE );
5468 * Commonal helper function
5470 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5473 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5474 g_snprintf(str, 64, tmp_str, changed);
5475 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5478 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5480 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5481 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5483 if ( !trw_layer_dem_test ( vtl, vlp ) )
5487 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5489 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5491 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5495 GHashTableIter iter;
5496 gpointer key, value;
5498 g_hash_table_iter_init ( &iter, vtl->waypoints );
5499 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5500 VikWaypoint *wp = VIK_WAYPOINT(value);
5501 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5504 wp_changed_message ( vtl, changed );
5507 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5509 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5510 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5512 if ( !trw_layer_dem_test ( vtl, vlp ) )
5516 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5518 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5520 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5524 GHashTableIter iter;
5525 gpointer key, value;
5527 g_hash_table_iter_init ( &iter, vtl->waypoints );
5528 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5529 VikWaypoint *wp = VIK_WAYPOINT(value);
5530 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5533 wp_changed_message ( vtl, changed );
5536 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5538 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5540 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5541 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5543 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5547 if ( !track->trackpoints )
5549 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5552 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5554 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5556 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5557 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5559 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5564 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5567 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5570 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5572 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5574 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5575 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5577 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5582 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5585 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5588 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5590 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5592 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5593 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5595 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5600 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5603 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5607 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5609 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5611 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5613 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5614 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5616 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5618 if ( trk && trk->trackpoints )
5620 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5621 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5622 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5623 if ( values[MA_VLP] )
5624 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5626 vik_layer_emit_update ( VIK_LAYER(vtl) );
5631 * Refine the selected track/route with a routing engine.
5632 * The routing engine is selected by the user, when requestiong the job.
5634 static void trw_layer_route_refine ( menu_array_sublayer values )
5636 static gint last_engine = 0;
5637 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5640 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5641 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5643 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5645 if ( trk && trk->trackpoints )
5647 /* Check size of the route */
5648 int nb = vik_track_get_tp_count(trk);
5650 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5651 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5652 GTK_MESSAGE_WARNING,
5653 GTK_BUTTONS_OK_CANCEL,
5654 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5656 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5657 gtk_widget_destroy ( dialog );
5658 if (response != GTK_RESPONSE_OK )
5661 /* Select engine from dialog */
5662 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5663 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5664 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5666 GTK_RESPONSE_REJECT,
5668 GTK_RESPONSE_ACCEPT,
5670 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5671 gtk_widget_show_all(label);
5673 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5675 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5676 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5677 gtk_widget_show_all(combo);
5679 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5681 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5683 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5685 /* Dialog validated: retrieve selected engine and do the job */
5686 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5687 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5690 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5692 /* Force saving track */
5693 /* FIXME: remove or rename this hack */
5694 vtl->route_finder_check_added_track = TRUE;
5697 vik_routing_engine_refine (routing, vtl, trk);
5699 /* FIXME: remove or rename this hack */
5700 if ( vtl->route_finder_added_track )
5701 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5703 vtl->route_finder_added_track = NULL;
5704 vtl->route_finder_check_added_track = FALSE;
5706 vik_layer_emit_update ( VIK_LAYER(vtl) );
5708 /* Restore cursor */
5709 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5711 gtk_widget_destroy ( dialog );
5715 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5717 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5718 trw_layer_tpwin_init ( vtl );
5721 /*************************************
5722 * merge/split by time routines
5723 *************************************/
5725 /* called for each key in track hash table.
5726 * If the current track has the same time stamp type, add it to the result,
5727 * except the one pointed by "exclude".
5728 * set exclude to NULL if there is no exclude to check.
5729 * Note that the result is in reverse (for performance reasons).
5734 gboolean with_timestamps;
5736 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5738 twt_udata *user_data = udata;
5739 VikTrackpoint *p1, *p2;
5740 VikTrack *trk = VIK_TRACK(value);
5741 if (trk == user_data->exclude) {
5745 if (trk->trackpoints) {
5746 p1 = vik_track_get_tp_first(trk);
5747 p2 = vik_track_get_tp_last(trk);
5749 if ( user_data->with_timestamps ) {
5750 if (!p1->has_timestamp || !p2->has_timestamp) {
5755 // Don't add tracks with timestamps when getting non timestamp tracks
5756 if (p1->has_timestamp || p2->has_timestamp) {
5762 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5766 * find_nearby_tracks_by_time:
5768 * Called for each track in track hash table.
5769 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5770 * to the current track, then the current track is added to the list in user_data[0]
5772 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5774 VikTrack *trk = VIK_TRACK(value);
5776 GList **nearby_tracks = ((gpointer *)user_data)[0];
5777 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5779 if ( !orig_trk || !orig_trk->trackpoints )
5783 * detect reasons for not merging, and return
5784 * if no reason is found not to merge, then do it.
5787 twt_udata *udata = user_data;
5788 // Exclude the original track from the compiled list
5789 if (trk == udata->exclude) {
5793 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5794 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5796 if (trk->trackpoints) {
5798 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5799 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5801 if (!p1->has_timestamp || !p2->has_timestamp) {
5802 //g_print("no timestamp\n");
5806 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5807 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5808 if (! (labs(t1 - p2->timestamp) < threshold ||
5810 labs(p1->timestamp - t2) < threshold)
5817 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5820 /* comparison function used to sort tracks; a and b are hash table keys */
5821 /* Not actively used - can be restored if needed
5822 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5824 GHashTable *tracks = user_data;
5827 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5828 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5830 if (t1 < t2) return -1;
5831 if (t1 > t2) return 1;
5836 /* comparison function used to sort trackpoints */
5837 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5839 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5841 if (t1 < t2) return -1;
5842 if (t1 > t2) return 1;
5847 * comparison function which can be used to sort tracks or waypoints by name
5849 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5851 const gchar* namea = (const gchar*) a;
5852 const gchar* nameb = (const gchar*) b;
5853 if ( namea == NULL || nameb == NULL)
5856 // Same sort method as used in the vik_treeview_*_alphabetize functions
5857 return strcmp ( namea, nameb );
5861 * Attempt to merge selected track with other tracks specified by the user
5862 * Tracks to merge with must be of the same 'type' as the selected track -
5863 * either all with timestamps, or all without timestamps
5865 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5867 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5868 GList *other_tracks = NULL;
5869 GHashTable *ght_tracks;
5870 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5871 ght_tracks = vtl->routes;
5873 ght_tracks = vtl->tracks;
5875 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5880 if ( !track->trackpoints )
5884 udata.result = &other_tracks;
5885 udata.exclude = track;
5886 // Allow merging with 'similar' time type time tracks
5887 // i.e. either those times, or those without
5888 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5890 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5891 other_tracks = g_list_reverse(other_tracks);
5893 if ( !other_tracks ) {
5894 if ( udata.with_timestamps )
5895 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5897 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5901 // Sort alphabetically for user presentation
5902 // Convert into list of names for usage with dialog function
5903 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5904 GList *other_tracks_names = NULL;
5905 GList *iter = g_list_first ( other_tracks );
5907 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5908 iter = g_list_next ( iter );
5911 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5913 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5917 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5918 g_list_free(other_tracks);
5919 g_list_free(other_tracks_names);
5924 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5925 VikTrack *merge_track;
5926 if ( track->is_route )
5927 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5929 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5932 vik_track_steal_and_append_trackpoints ( track, merge_track );
5933 if ( track->is_route )
5934 vik_trw_layer_delete_route (vtl, merge_track);
5936 vik_trw_layer_delete_track (vtl, merge_track);
5937 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5940 for (l = merge_list; l != NULL; l = g_list_next(l))
5942 g_list_free(merge_list);
5944 vik_layer_emit_update( VIK_LAYER(vtl) );
5948 // c.f. trw_layer_sorted_track_id_by_name_list
5949 // but don't add the specified track to the list (normally current track)
5950 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5952 twt_udata *user_data = udata;
5955 if (trk == user_data->exclude) {
5959 // Sort named list alphabetically
5960 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5964 * Join - this allows combining 'tracks' and 'track routes'
5965 * i.e. doesn't care about whether tracks have consistent timestamps
5966 * ATM can only append one track at a time to the currently selected track
5968 static void trw_layer_append_track ( menu_array_sublayer values )
5971 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5973 GHashTable *ght_tracks;
5974 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5975 ght_tracks = vtl->routes;
5977 ght_tracks = vtl->tracks;
5979 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5984 GList *other_tracks_names = NULL;
5986 // Sort alphabetically for user presentation
5987 // Convert into list of names for usage with dialog function
5988 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5990 udata.result = &other_tracks_names;
5991 udata.exclude = trk;
5993 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5995 // Note the limit to selecting one track only
5996 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5997 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5998 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6001 trk->is_route ? _("Append Route"): _("Append Track"),
6002 trk->is_route ? _("Select the route to append after the current route") :
6003 _("Select the track to append after the current track") );
6005 g_list_free(other_tracks_names);
6007 // It's a list, but shouldn't contain more than one other track!
6008 if ( append_list ) {
6010 for (l = append_list; l != NULL; l = g_list_next(l)) {
6011 // TODO: at present this uses the first track found by name,
6012 // which with potential multiple same named tracks may not be the one selected...
6013 VikTrack *append_track;
6014 if ( trk->is_route )
6015 append_track = vik_trw_layer_get_route ( vtl, l->data );
6017 append_track = vik_trw_layer_get_track ( vtl, l->data );
6019 if ( append_track ) {
6020 vik_track_steal_and_append_trackpoints ( trk, append_track );
6021 if ( trk->is_route )
6022 vik_trw_layer_delete_route (vtl, append_track);
6024 vik_trw_layer_delete_track (vtl, append_track);
6027 for (l = append_list; l != NULL; l = g_list_next(l))
6029 g_list_free(append_list);
6031 vik_layer_emit_update( VIK_LAYER(vtl) );
6036 * Very similar to trw_layer_append_track for joining
6037 * but this allows selection from the 'other' list
6038 * If a track is selected, then is shows routes and joins the selected one
6039 * If a route is selected, then is shows tracks and joins the selected one
6041 static void trw_layer_append_other ( menu_array_sublayer values )
6044 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6046 GHashTable *ght_mykind, *ght_others;
6047 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6048 ght_mykind = vtl->routes;
6049 ght_others = vtl->tracks;
6052 ght_mykind = vtl->tracks;
6053 ght_others = vtl->routes;
6056 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6061 GList *other_tracks_names = NULL;
6063 // Sort alphabetically for user presentation
6064 // Convert into list of names for usage with dialog function
6065 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6067 udata.result = &other_tracks_names;
6068 udata.exclude = trk;
6070 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6072 // Note the limit to selecting one track only
6073 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6074 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6075 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6078 trk->is_route ? _("Append Track"): _("Append Route"),
6079 trk->is_route ? _("Select the track to append after the current route") :
6080 _("Select the route to append after the current track") );
6082 g_list_free(other_tracks_names);
6084 // It's a list, but shouldn't contain more than one other track!
6085 if ( append_list ) {
6087 for (l = append_list; l != NULL; l = g_list_next(l)) {
6088 // TODO: at present this uses the first track found by name,
6089 // which with potential multiple same named tracks may not be the one selected...
6091 // Get FROM THE OTHER TYPE list
6092 VikTrack *append_track;
6093 if ( trk->is_route )
6094 append_track = vik_trw_layer_get_track ( vtl, l->data );
6096 append_track = vik_trw_layer_get_route ( vtl, l->data );
6098 if ( append_track ) {
6100 if ( !append_track->is_route &&
6101 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6102 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6104 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6105 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6106 vik_track_merge_segments ( append_track );
6107 vik_track_to_routepoints ( append_track );
6114 vik_track_steal_and_append_trackpoints ( trk, append_track );
6116 // Delete copied which is FROM THE OTHER TYPE list
6117 if ( trk->is_route )
6118 vik_trw_layer_delete_track (vtl, append_track);
6120 vik_trw_layer_delete_route (vtl, append_track);
6123 for (l = append_list; l != NULL; l = g_list_next(l))
6125 g_list_free(append_list);
6126 vik_layer_emit_update( VIK_LAYER(vtl) );
6130 /* merge by segments */
6131 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6133 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6134 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6135 guint segments = vik_track_merge_segments ( trk );
6136 // NB currently no need to redraw as segments not actually shown on the display
6137 // However inform the user of what happened:
6139 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6140 g_snprintf(str, 64, tmp_str, segments);
6141 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6144 /* merge by time routine */
6145 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6147 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6151 GList *tracks_with_timestamp = NULL;
6152 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6153 if (orig_trk->trackpoints &&
6154 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6155 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6160 udata.result = &tracks_with_timestamp;
6161 udata.exclude = orig_trk;
6162 udata.with_timestamps = TRUE;
6163 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6164 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6166 if (!tracks_with_timestamp) {
6167 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6170 g_list_free(tracks_with_timestamp);
6172 static guint threshold_in_minutes = 1;
6173 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6174 _("Merge Threshold..."),
6175 _("Merge when time between tracks less than:"),
6176 &threshold_in_minutes)) {
6180 // keep attempting to merge all tracks until no merges within the time specified is possible
6181 gboolean attempt_merge = TRUE;
6182 GList *nearby_tracks = NULL;
6184 static gpointer params[3];
6186 while ( attempt_merge ) {
6188 // Don't try again unless tracks have changed
6189 attempt_merge = FALSE;
6191 trps = orig_trk->trackpoints;
6195 if (nearby_tracks) {
6196 g_list_free(nearby_tracks);
6197 nearby_tracks = NULL;
6200 params[0] = &nearby_tracks;
6201 params[1] = orig_trk;
6202 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6204 /* get a list of adjacent-in-time tracks */
6205 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6208 GList *l = nearby_tracks;
6210 /* remove trackpoints from merged track, delete track */
6211 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6212 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6214 // Tracks have changed, therefore retry again against all the remaining tracks
6215 attempt_merge = TRUE;
6220 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6223 g_list_free(nearby_tracks);
6225 vik_layer_emit_update( VIK_LAYER(vtl) );
6229 * Split a track at the currently selected trackpoint
6231 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6233 if ( !vtl->current_tpl )
6236 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6237 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6239 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6240 GList *newglist = g_list_alloc ();
6241 newglist->prev = NULL;
6242 newglist->next = vtl->current_tpl->next;
6243 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6244 tr->trackpoints = newglist;
6246 vtl->current_tpl->next->prev = newglist; /* end old track here */
6247 vtl->current_tpl->next = NULL;
6249 // Bounds of the selected track changed due to the split
6250 vik_track_calculate_bounds ( vtl->current_tp_track );
6252 vtl->current_tpl = newglist; /* change tp to first of new track. */
6253 vtl->current_tp_track = tr;
6256 vik_trw_layer_add_route ( vtl, name, tr );
6258 vik_trw_layer_add_track ( vtl, name, tr );
6260 // Bounds of the new track created by the split
6261 vik_track_calculate_bounds ( tr );
6267 // Also need id of newly created track
6270 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6272 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6274 if ( trkf && udata.uuid )
6275 vtl->current_tp_id = udata.uuid;
6277 vtl->current_tp_id = NULL;
6279 vik_layer_emit_update(VIK_LAYER(vtl));
6285 /* split by time routine */
6286 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6288 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6289 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6290 GList *trps = track->trackpoints;
6292 GList *newlists = NULL;
6293 GList *newtps = NULL;
6294 static guint thr = 1;
6301 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6302 _("Split Threshold..."),
6303 _("Split when time between trackpoints exceeds:"),
6308 /* iterate through trackpoints, and copy them into new lists without touching original list */
6309 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6313 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6315 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6318 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6319 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6320 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6322 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6327 if (ts - prev_ts > thr*60) {
6328 /* flush accumulated trackpoints into new list */
6329 newlists = g_list_append(newlists, g_list_reverse(newtps));
6333 /* accumulate trackpoint copies in newtps, in reverse order */
6334 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6336 iter = g_list_next(iter);
6339 newlists = g_list_append(newlists, g_list_reverse(newtps));
6342 /* put lists of trackpoints into tracks */
6344 // Only bother updating if the split results in new tracks
6345 if (g_list_length (newlists) > 1) {
6350 tr = vik_track_copy ( track, FALSE );
6351 tr->trackpoints = (GList *)(iter->data);
6353 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6354 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6355 g_free ( new_tr_name );
6356 vik_track_calculate_bounds ( tr );
6357 iter = g_list_next(iter);
6359 // Remove original track and then update the display
6360 vik_trw_layer_delete_track (vtl, track);
6361 vik_layer_emit_update(VIK_LAYER(vtl));
6363 g_list_free(newlists);
6367 * Split a track by the number of points as specified by the user
6369 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6371 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6373 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6374 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6376 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6381 // Check valid track
6382 GList *trps = track->trackpoints;
6386 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6387 _("Split Every Nth Point"),
6388 _("Split on every Nth point:"),
6389 250, // Default value as per typical limited track capacity of various GPS devices
6393 // Was a valid number returned?
6399 GList *newlists = NULL;
6400 GList *newtps = NULL;
6405 /* accumulate trackpoint copies in newtps, in reverse order */
6406 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6408 if (count >= points) {
6409 /* flush accumulated trackpoints into new list */
6410 newlists = g_list_append(newlists, g_list_reverse(newtps));
6414 iter = g_list_next(iter);
6417 // If there is a remaining chunk put that into the new split list
6418 // This may well be the whole track if no split points were encountered
6420 newlists = g_list_append(newlists, g_list_reverse(newtps));
6423 /* put lists of trackpoints into tracks */
6425 // Only bother updating if the split results in new tracks
6426 if (g_list_length (newlists) > 1) {
6431 tr = vik_track_copy ( track, FALSE );
6432 tr->trackpoints = (GList *)(iter->data);
6434 if ( track->is_route ) {
6435 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6436 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6439 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6440 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6442 g_free ( new_tr_name );
6443 vik_track_calculate_bounds ( tr );
6445 iter = g_list_next(iter);
6447 // Remove original track and then update the display
6448 if ( track->is_route )
6449 vik_trw_layer_delete_route (vtl, track);
6451 vik_trw_layer_delete_track (vtl, track);
6452 vik_layer_emit_update(VIK_LAYER(vtl));
6454 g_list_free(newlists);
6458 * Split a track at the currently selected trackpoint
6460 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6462 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6463 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6464 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6468 * Split a track by its segments
6469 * Routes do not have segments so don't call this for routes
6471 static void trw_layer_split_segments ( menu_array_sublayer values )
6473 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6474 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6481 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6484 for ( i = 0; i < ntracks; i++ ) {
6486 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6487 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6488 g_free ( new_tr_name );
6493 // Remove original track
6494 vik_trw_layer_delete_track ( vtl, trk );
6495 vik_layer_emit_update ( VIK_LAYER(vtl) );
6498 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6501 /* end of split/merge routines */
6503 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6507 // Find available adjacent trackpoint
6508 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6509 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6510 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6512 // Delete current trackpoint
6513 vik_trackpoint_free ( vtl->current_tpl->data );
6514 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6516 // Set to current to the available adjacent trackpoint
6517 vtl->current_tpl = new_tpl;
6519 if ( vtl->current_tp_track ) {
6520 vik_track_calculate_bounds ( vtl->current_tp_track );
6524 // Delete current trackpoint
6525 vik_trackpoint_free ( vtl->current_tpl->data );
6526 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6527 trw_layer_cancel_current_tp ( vtl, FALSE );
6532 * Delete the selected point
6534 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6536 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6538 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6539 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6541 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6546 if ( !vtl->current_tpl )
6549 trw_layer_trackpoint_selected_delete ( vtl, trk );
6551 // Track has been updated so update tps:
6552 trw_layer_cancel_tps_of_track ( vtl, trk );
6554 vik_layer_emit_update ( VIK_LAYER(vtl) );
6558 * Delete adjacent track points at the same position
6559 * AKA Delete Dulplicates on the Properties Window
6561 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6563 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6565 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6566 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6568 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6573 gulong removed = vik_track_remove_dup_points ( trk );
6575 // Track has been updated so update tps:
6576 trw_layer_cancel_tps_of_track ( vtl, trk );
6578 // Inform user how much was deleted as it's not obvious from the normal view
6580 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6581 g_snprintf(str, 64, tmp_str, removed);
6582 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6584 vik_layer_emit_update ( VIK_LAYER(vtl) );
6588 * Delete adjacent track points with the same timestamp
6589 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6591 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6593 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6595 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6596 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6598 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6603 gulong removed = vik_track_remove_same_time_points ( trk );
6605 // Track has been updated so update tps:
6606 trw_layer_cancel_tps_of_track ( vtl, trk );
6608 // Inform user how much was deleted as it's not obvious from the normal view
6610 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6611 g_snprintf(str, 64, tmp_str, removed);
6612 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6614 vik_layer_emit_update ( VIK_LAYER(vtl) );
6620 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6622 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6624 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6625 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6627 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6632 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6634 vik_layer_emit_update ( VIK_LAYER(vtl) );
6637 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6639 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6641 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6642 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6644 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6649 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6651 vik_layer_emit_update ( VIK_LAYER(vtl) );
6657 static void trw_layer_reverse ( menu_array_sublayer values )
6659 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6661 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6662 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6664 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6669 vik_track_reverse ( track );
6671 vik_layer_emit_update ( VIK_LAYER(vtl) );
6675 * Open a program at the specified date
6676 * Mainly for RedNotebook - http://rednotebook.sourceforge.net/
6677 * But could work with any program that accepts a command line of --date=<date>
6678 * FUTURE: Allow configuring of command line options + date format
6680 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6683 gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str );
6684 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6685 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program );
6686 g_error_free ( err );
6692 * Open a diary at the date of the track or waypoint
6694 static void trw_layer_diary ( menu_array_sublayer values )
6696 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6698 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6699 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6705 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6706 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6707 trw_layer_diary_open ( vtl, date_buf );
6710 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6712 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6713 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6719 if ( wpt->has_timestamp ) {
6720 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6721 trw_layer_diary_open ( vtl, date_buf );
6724 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6729 * Open a program at the specified date
6730 * Mainly for Stellarium - http://stellarium.org/
6731 * But could work with any program that accepts the same command line options...
6732 * FUTURE: Allow configuring of command line options + format or parameters
6734 static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, const gchar *time_str, const gchar *lat_str, const gchar *lon_str, const gchar *alt_str )
6738 g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL );
6739 gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
6740 astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
6741 g_warning ( "%s", cmd );
6742 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6743 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program );
6744 g_warning ( "%s", err->message );
6745 g_error_free ( err );
6747 util_add_to_deletion_list ( tmp );
6752 // Format of stellarium lat & lon seems designed to be particularly awkward
6753 // who uses ' & " in the parameters for the command line?!
6756 static gchar *convert_to_dms ( gdouble dec )
6762 gchar *result = NULL;
6776 tmp = (tmp - val_d) * 60;
6780 val_s = (tmp - val_m) * 60;
6783 result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
6788 * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
6790 static void trw_layer_astro ( menu_array_sublayer values )
6792 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6794 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6795 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6799 VikTrackpoint *tp = NULL;
6800 if ( vtl->current_tpl )
6801 // Current Trackpoint
6802 tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6803 else if ( trk->trackpoints )
6804 // Otherwise first trackpoint
6805 tp = VIK_TRACKPOINT(trk->trackpoints->data);
6810 if ( tp->has_timestamp ) {
6812 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
6814 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
6816 vik_coord_to_latlon ( &tp->coord, &ll );
6817 gchar *lat_str = convert_to_dms ( ll.lat );
6818 gchar *lon_str = convert_to_dms ( ll.lon );
6820 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) );
6821 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf);
6826 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6828 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6829 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6833 if ( wpt->has_timestamp ) {
6835 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
6837 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
6839 vik_coord_to_latlon ( &wpt->coord, &ll );
6840 gchar *lat_str = convert_to_dms ( ll.lat );
6841 gchar *lon_str = convert_to_dms ( ll.lon );
6843 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) );
6844 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf );
6849 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6854 * Similar to trw_layer_enum_item, but this uses a sorted method
6857 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6859 GList **list = (GList**)udata;
6860 // *list = g_list_prepend(*all, key); //unsorted method
6861 // Sort named list alphabetically
6862 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6867 * Now Waypoint specific sort
6869 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6871 GList **list = (GList**)udata;
6872 // Sort named list alphabetically
6873 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6877 * Track specific sort
6879 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6881 GList **list = (GList**)udata;
6882 // Sort named list alphabetically
6883 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6888 gboolean has_same_track_name;
6889 const gchar *same_track_name;
6890 } same_track_name_udata;
6892 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6894 const gchar* namea = (const gchar*) aa;
6895 const gchar* nameb = (const gchar*) bb;
6898 gint result = strcmp ( namea, nameb );
6900 if ( result == 0 ) {
6901 // Found two names the same
6902 same_track_name_udata *user_data = udata;
6903 user_data->has_same_track_name = TRUE;
6904 user_data->same_track_name = namea;
6907 // Leave ordering the same
6912 * Find out if any tracks have the same name in this hash table
6914 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6916 // Sort items by name, then compare if any next to each other are the same
6918 GList *track_names = NULL;
6919 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6922 if ( ! track_names )
6925 same_track_name_udata udata;
6926 udata.has_same_track_name = FALSE;
6928 // Use sort routine to traverse list comparing items
6929 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6930 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6931 // Still no tracks...
6935 return udata.has_same_track_name;
6939 * Force unqiue track names for the track table specified
6940 * Note the panel is a required parameter to enable the update of the names displayed
6941 * Specify if on tracks or else on routes
6943 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6945 // . Search list for an instance of repeated name
6946 // . get track of this name
6947 // . create new name
6948 // . rename track & update equiv. treeview iter
6949 // . repeat until all different
6951 same_track_name_udata udata;
6953 GList *track_names = NULL;
6954 udata.has_same_track_name = FALSE;
6955 udata.same_track_name = NULL;
6957 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6960 if ( ! track_names )
6963 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6965 // Still no tracks...
6966 if ( ! dummy_list1 )
6969 while ( udata.has_same_track_name ) {
6971 // Find a track with the same name
6974 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6976 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6980 g_critical("Houston, we've had a problem.");
6981 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6982 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6987 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6988 vik_track_set_name ( trk, newname );
6994 // Need want key of it for treeview update
6995 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6997 if ( trkf && udataU.uuid ) {
7001 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7003 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7006 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
7008 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
7010 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
7014 // Start trying to find same names again...
7016 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7017 udata.has_same_track_name = FALSE;
7018 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7020 // No tracks any more - give up searching
7021 if ( ! dummy_list2 )
7022 udata.has_same_track_name = FALSE;
7026 vik_layers_panel_emit_update ( vlp );
7029 static void trw_layer_sort_order_specified ( VikTrwLayer *vtl, guint sublayer_type, vik_layer_sort_order_t order )
7033 switch (sublayer_type) {
7034 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
7035 iter = &(vtl->tracks_iter);
7036 vtl->track_sort_order = order;
7038 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7039 iter = &(vtl->routes_iter);
7040 vtl->track_sort_order = order;
7042 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7043 iter = &(vtl->waypoints_iter);
7044 vtl->wp_sort_order = order;
7048 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, order );
7051 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
7053 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7054 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_ASCENDING );
7057 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
7059 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7060 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING );
7063 static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values )
7065 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7066 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING );
7069 static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values )
7071 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7072 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING );
7078 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
7080 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7083 // Ensure list of track names offered is unique
7084 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
7085 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7086 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7087 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
7093 // Sort list alphabetically for better presentation
7094 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7097 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
7101 // Get list of items to delete from the user
7102 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7105 _("Delete Selection"),
7106 _("Select tracks to delete"));
7109 // Delete requested tracks
7110 // since specificly requested, IMHO no need for extra confirmation
7111 if ( delete_list ) {
7113 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7114 // This deletes first trk it finds of that name (but uniqueness is enforced above)
7115 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
7117 g_list_free(delete_list);
7118 // Reset layer timestamps in case they have now changed
7119 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7121 vik_layer_emit_update( VIK_LAYER(vtl) );
7128 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
7130 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7133 // Ensure list of track names offered is unique
7134 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
7135 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7136 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7137 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
7143 // Sort list alphabetically for better presentation
7144 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7147 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
7151 // Get list of items to delete from the user
7152 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7155 _("Delete Selection"),
7156 _("Select routes to delete") );
7159 // Delete requested routes
7160 // since specificly requested, IMHO no need for extra confirmation
7161 if ( delete_list ) {
7163 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7164 // This deletes first route it finds of that name (but uniqueness is enforced above)
7165 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
7167 g_list_free(delete_list);
7168 vik_layer_emit_update( VIK_LAYER(vtl) );
7173 gboolean has_same_waypoint_name;
7174 const gchar *same_waypoint_name;
7175 } same_waypoint_name_udata;
7177 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
7179 const gchar* namea = (const gchar*) aa;
7180 const gchar* nameb = (const gchar*) bb;
7183 gint result = strcmp ( namea, nameb );
7185 if ( result == 0 ) {
7186 // Found two names the same
7187 same_waypoint_name_udata *user_data = udata;
7188 user_data->has_same_waypoint_name = TRUE;
7189 user_data->same_waypoint_name = namea;
7192 // Leave ordering the same
7197 * Find out if any waypoints have the same name in this layer
7199 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
7201 // Sort items by name, then compare if any next to each other are the same
7203 GList *waypoint_names = NULL;
7204 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7207 if ( ! waypoint_names )
7210 same_waypoint_name_udata udata;
7211 udata.has_same_waypoint_name = FALSE;
7213 // Use sort routine to traverse list comparing items
7214 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7215 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7216 // Still no waypoints...
7220 return udata.has_same_waypoint_name;
7224 * Force unqiue waypoint names for this layer
7225 * Note the panel is a required parameter to enable the update of the names displayed
7227 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7229 // . Search list for an instance of repeated name
7230 // . get waypoint of this name
7231 // . create new name
7232 // . rename waypoint & update equiv. treeview iter
7233 // . repeat until all different
7235 same_waypoint_name_udata udata;
7237 GList *waypoint_names = NULL;
7238 udata.has_same_waypoint_name = FALSE;
7239 udata.same_waypoint_name = NULL;
7241 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7244 if ( ! waypoint_names )
7247 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7249 // Still no waypoints...
7250 if ( ! dummy_list1 )
7253 while ( udata.has_same_waypoint_name ) {
7255 // Find a waypoint with the same name
7256 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7260 g_critical("Houston, we've had a problem.");
7261 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7262 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7267 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7269 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7271 // Start trying to find same names again...
7272 waypoint_names = NULL;
7273 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7274 udata.has_same_waypoint_name = FALSE;
7275 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7277 // No waypoints any more - give up searching
7278 if ( ! dummy_list2 )
7279 udata.has_same_waypoint_name = FALSE;
7283 vik_layers_panel_emit_update ( vlp );
7289 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7291 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7294 // Ensure list of waypoint names offered is unique
7295 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7296 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7297 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7298 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7304 // Sort list alphabetically for better presentation
7305 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7307 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7311 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7313 // Get list of items to delete from the user
7314 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7317 _("Delete Selection"),
7318 _("Select waypoints to delete"));
7321 // Delete requested waypoints
7322 // since specificly requested, IMHO no need for extra confirmation
7323 if ( delete_list ) {
7325 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7326 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7327 trw_layer_delete_waypoint_by_name (vtl, l->data);
7329 g_list_free(delete_list);
7331 trw_layer_calculate_bounds_waypoints ( vtl );
7332 // Reset layer timestamp in case it has now changed
7333 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7334 vik_layer_emit_update( VIK_LAYER(vtl) );
7342 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7344 vik_treeview_item_toggle_visible ( vt, it );
7350 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7352 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7358 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7360 wp->visible = GPOINTER_TO_INT (on_off);
7366 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7368 wp->visible = !wp->visible;
7374 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7376 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7377 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7378 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7379 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7381 vik_layer_emit_update ( VIK_LAYER(vtl) );
7387 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7389 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7390 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7391 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7392 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7394 vik_layer_emit_update ( VIK_LAYER(vtl) );
7400 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7402 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7403 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7404 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7406 vik_layer_emit_update ( VIK_LAYER(vtl) );
7412 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7414 trk->visible = GPOINTER_TO_INT (on_off);
7420 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7422 trk->visible = !trk->visible;
7428 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7430 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7431 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7432 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7433 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7435 vik_layer_emit_update ( VIK_LAYER(vtl) );
7441 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7443 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7444 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7445 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7446 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7448 vik_layer_emit_update ( VIK_LAYER(vtl) );
7454 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7456 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7457 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7458 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7460 vik_layer_emit_update ( VIK_LAYER(vtl) );
7466 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7468 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7469 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7470 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7471 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7473 vik_layer_emit_update ( VIK_LAYER(vtl) );
7479 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7481 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7482 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7483 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7484 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7486 vik_layer_emit_update ( VIK_LAYER(vtl) );
7492 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7494 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7495 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7496 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7498 vik_layer_emit_update ( VIK_LAYER(vtl) );
7502 * vik_trw_layer_build_waypoint_list_t:
7504 * Helper function to construct a list of #vik_trw_waypoint_list_t
7506 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7508 GList *waypoints_and_layers = NULL;
7509 // build waypoints_and_layers list
7510 while ( waypoints ) {
7511 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7512 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7514 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7515 waypoints = g_list_next ( waypoints );
7517 return waypoints_and_layers;
7521 * trw_layer_create_waypoint_list:
7523 * Create the latest list of waypoints with the associated layer(s)
7524 * Although this will always be from a single layer here
7526 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7528 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7529 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7531 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7535 * trw_layer_analyse_close:
7537 * Stuff to do on dialog closure
7539 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7541 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7542 gtk_widget_destroy ( dialog );
7543 vtl->tracks_analysis_dialog = NULL;
7547 * vik_trw_layer_build_track_list_t:
7549 * Helper function to construct a list of #vik_trw_track_list_t
7551 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7553 GList *tracks_and_layers = NULL;
7554 // build tracks_and_layers list
7556 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7557 vtdl->trk = VIK_TRACK(tracks->data);
7559 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7560 tracks = g_list_next ( tracks );
7562 return tracks_and_layers;
7566 * trw_layer_create_track_list:
7568 * Create the latest list of tracks with the associated layer(s)
7569 * Although this will always be from a single layer here
7571 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7573 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7574 GList *tracks = NULL;
7575 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7576 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7578 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7580 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7583 static void trw_layer_tracks_stats ( menu_array_layer values )
7585 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7586 // There can only be one!
7587 if ( vtl->tracks_analysis_dialog )
7590 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7591 VIK_LAYER(vtl)->name,
7593 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7594 trw_layer_create_track_list,
7595 trw_layer_analyse_close );
7601 static void trw_layer_routes_stats ( menu_array_layer values )
7603 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7604 // There can only be one!
7605 if ( vtl->tracks_analysis_dialog )
7608 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7609 VIK_LAYER(vtl)->name,
7611 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7612 trw_layer_create_track_list,
7613 trw_layer_analyse_close );
7616 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7618 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7619 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7621 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7624 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7626 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7627 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7630 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7631 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7635 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7637 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7638 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7642 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7643 } else if ( !strncmp(wp->comment, "http", 4) ) {
7644 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7645 } else if ( !strncmp(wp->description, "http", 4) ) {
7646 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7650 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7652 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7654 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7656 // No actual change to the name supplied
7658 if (strcmp(newname, wp->name) == 0 )
7661 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7664 // An existing waypoint has been found with the requested name
7665 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7666 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7671 // Update WP name and refresh the treeview
7672 vik_waypoint_set_name (wp, newname);
7674 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7675 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7677 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7682 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7684 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7686 // No actual change to the name supplied
7688 if (strcmp(newname, trk->name) == 0)
7691 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7694 // An existing track has been found with the requested name
7695 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7696 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7700 // Update track name and refresh GUI parts
7701 vik_track_set_name (trk, newname);
7703 // Update any subwindows that could be displaying this track which has changed name
7704 // Only one Track Edit Window
7705 if ( l->current_tp_track == trk && l->tpwin ) {
7706 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7708 // Property Dialog of the track
7709 vik_trw_layer_propwin_update ( trk );
7711 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7712 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7714 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7719 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7721 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7723 // No actual change to the name supplied
7725 if (strcmp(newname, trk->name) == 0)
7728 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7731 // An existing track has been found with the requested name
7732 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7733 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7737 // Update track name and refresh GUI parts
7738 vik_track_set_name (trk, newname);
7740 // Update any subwindows that could be displaying this track which has changed name
7741 // Only one Track Edit Window
7742 if ( l->current_tp_track == trk && l->tpwin ) {
7743 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7745 // Property Dialog of the track
7746 vik_trw_layer_propwin_update ( trk );
7748 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7749 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7751 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7758 static gboolean is_valid_geocache_name ( gchar *str )
7760 gint len = strlen ( str );
7761 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]));
7764 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7766 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7767 a_acquire_set_filter_track ( trk );
7770 #ifdef VIK_CONFIG_GOOGLE
7771 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7773 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7774 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7777 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7779 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7781 gchar *escaped = uri_escape ( tr->comment );
7782 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7783 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7790 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7791 /* viewpoint is now available instead */
7792 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7794 static menu_array_sublayer pass_along;
7796 gboolean rv = FALSE;
7798 pass_along[MA_VTL] = l;
7799 pass_along[MA_VLP] = vlp;
7800 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7801 pass_along[MA_SUBLAYER_ID] = sublayer;
7802 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7803 pass_along[MA_VVP] = vvp;
7804 pass_along[MA_TV_ITER] = iter;
7805 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7807 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7811 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7812 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7813 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7814 gtk_widget_show ( item );
7816 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7817 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7818 if (tr && tr->property_dialog)
7819 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7821 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7822 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7823 if (tr && tr->property_dialog)
7824 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7827 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7828 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7829 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7830 gtk_widget_show ( item );
7832 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7833 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7834 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7835 gtk_widget_show ( item );
7837 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7838 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7839 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7840 gtk_widget_show ( item );
7842 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7844 // Always create separator as now there is always at least the transform menu option
7845 item = gtk_menu_item_new ();
7846 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7847 gtk_widget_show ( item );
7849 /* could be a right-click using the tool */
7850 if ( vlp != NULL ) {
7851 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7852 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7853 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7854 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7855 gtk_widget_show ( item );
7858 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7860 if ( wp && wp->name ) {
7861 if ( is_valid_geocache_name ( wp->name ) ) {
7862 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7863 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7864 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7865 gtk_widget_show ( item );
7867 #ifdef VIK_CONFIG_GEOTAG
7868 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7869 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7870 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7871 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7872 gtk_widget_show ( item );
7876 if ( wp && wp->image )
7878 // Set up image paramater
7879 pass_along[MA_MISC] = wp->image;
7881 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7882 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
7883 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7884 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7885 gtk_widget_show ( item );
7887 #ifdef VIK_CONFIG_GEOTAG
7888 GtkWidget *geotag_submenu = gtk_menu_new ();
7889 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7890 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7891 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7892 gtk_widget_show ( item );
7893 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7895 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7896 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7897 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7898 gtk_widget_show ( item );
7900 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7902 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7903 gtk_widget_show ( item );
7910 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7911 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7912 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7913 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7915 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7916 gtk_widget_show ( item );
7922 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7923 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7924 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7925 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7926 gtk_widget_show ( item );
7927 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7928 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7929 gtk_widget_set_sensitive ( item, TRUE );
7931 gtk_widget_set_sensitive ( item, FALSE );
7934 item = gtk_menu_item_new ();
7935 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7936 gtk_widget_show ( item );
7939 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7942 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7943 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7944 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7945 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7946 gtk_widget_show ( item );
7949 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7951 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7952 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7953 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7954 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7955 gtk_widget_show ( item );
7957 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7958 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7959 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7960 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7961 gtk_widget_show ( item );
7963 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7964 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7966 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7967 gtk_widget_show ( item );
7969 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7970 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7971 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7972 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7973 gtk_widget_show ( item );
7975 GtkWidget *vis_submenu = gtk_menu_new ();
7976 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7977 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7978 gtk_widget_show ( item );
7979 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7981 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7982 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7983 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7984 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7985 gtk_widget_show ( item );
7987 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7988 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7989 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7990 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7991 gtk_widget_show ( item );
7993 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7994 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7996 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7997 gtk_widget_show ( item );
7999 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
8000 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
8002 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8005 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
8009 if ( l->current_track && !l->current_track->is_route ) {
8010 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8011 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8012 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8013 gtk_widget_show ( item );
8015 item = gtk_menu_item_new ();
8016 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8017 gtk_widget_show ( item );
8020 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
8021 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8022 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
8023 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8024 gtk_widget_show ( item );
8026 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
8027 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
8029 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8030 gtk_widget_show ( item );
8031 // Make it available only when a new track *not* already in progress
8032 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8034 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
8035 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
8037 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8038 gtk_widget_show ( item );
8040 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
8041 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
8043 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8044 gtk_widget_show ( item );
8046 GtkWidget *vis_submenu = gtk_menu_new ();
8047 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8048 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8049 gtk_widget_show ( item );
8050 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8052 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
8053 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8056 gtk_widget_show ( item );
8058 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
8059 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
8061 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8062 gtk_widget_show ( item );
8064 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8065 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
8067 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8069 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
8070 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8073 gtk_widget_show ( item );
8075 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
8077 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8078 gtk_widget_show ( item );
8081 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
8085 if ( l->current_track && l->current_track->is_route ) {
8086 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8087 // Reuse finish track method
8088 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8089 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8090 gtk_widget_show ( item );
8092 item = gtk_menu_item_new ();
8093 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8094 gtk_widget_show ( item );
8097 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
8098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
8100 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8101 gtk_widget_show ( item );
8103 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
8104 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8105 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
8106 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8107 gtk_widget_show ( item );
8108 // Make it available only when a new track *not* already in progress
8109 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8111 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
8112 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
8114 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8115 gtk_widget_show ( item );
8117 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
8118 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
8120 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8121 gtk_widget_show ( item );
8123 GtkWidget *vis_submenu = gtk_menu_new ();
8124 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8125 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8126 gtk_widget_show ( item );
8127 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8129 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
8130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
8132 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8133 gtk_widget_show ( item );
8135 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
8136 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8137 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
8138 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8139 gtk_widget_show ( item );
8141 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8142 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8143 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
8144 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8146 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
8147 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8149 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8151 gtk_widget_show ( item );
8153 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
8155 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8156 gtk_widget_show ( item );
8160 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8161 GtkWidget *submenu_sort = gtk_menu_new ();
8162 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
8163 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8164 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8165 gtk_widget_show ( item );
8166 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
8168 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
8169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
8171 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8172 gtk_widget_show ( item );
8174 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
8175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
8177 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8178 gtk_widget_show ( item );
8180 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") );
8181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along );
8183 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8184 gtk_widget_show ( item );
8186 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") );
8187 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8188 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along );
8189 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8190 gtk_widget_show ( item );
8193 GtkWidget *upload_submenu = gtk_menu_new ();
8195 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8197 item = gtk_menu_item_new ();
8198 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8199 gtk_widget_show ( item );
8201 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
8202 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8203 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
8204 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8205 if ( l->current_track ) {
8206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8207 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8208 gtk_widget_show ( item );
8211 item = gtk_menu_item_new ();
8212 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8213 gtk_widget_show ( item );
8216 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8217 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
8219 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
8220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8222 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8223 gtk_widget_show ( item );
8225 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8226 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8227 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8228 gtk_widget_show ( item );
8230 GtkWidget *goto_submenu;
8231 goto_submenu = gtk_menu_new ();
8232 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8234 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8235 gtk_widget_show ( item );
8236 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8238 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8239 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8240 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8241 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8242 gtk_widget_show ( item );
8244 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8245 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8246 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8247 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8248 gtk_widget_show ( item );
8250 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8251 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8253 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8254 gtk_widget_show ( item );
8256 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8257 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8259 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8260 gtk_widget_show ( item );
8262 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8263 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8264 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8265 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8266 gtk_widget_show ( item );
8268 // Routes don't have speeds
8269 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8270 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8271 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8272 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8273 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8274 gtk_widget_show ( item );
8277 GtkWidget *combine_submenu;
8278 combine_submenu = gtk_menu_new ();
8279 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8280 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8281 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8282 gtk_widget_show ( item );
8283 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8285 // Routes don't have times or segments...
8286 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8287 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8288 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8289 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8290 gtk_widget_show ( item );
8292 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8293 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8294 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8295 gtk_widget_show ( item );
8298 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8300 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8301 gtk_widget_show ( item );
8303 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8304 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8306 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8308 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8309 gtk_widget_show ( item );
8311 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8312 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8314 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8315 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8316 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8317 gtk_widget_show ( item );
8319 GtkWidget *split_submenu;
8320 split_submenu = gtk_menu_new ();
8321 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8322 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8323 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8324 gtk_widget_show ( item );
8325 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8327 // Routes don't have times or segments...
8328 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8329 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8330 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8331 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8332 gtk_widget_show ( item );
8334 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8335 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8337 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8338 gtk_widget_show ( item );
8341 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8342 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8343 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8344 gtk_widget_show ( item );
8346 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8348 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8349 gtk_widget_show ( item );
8350 // Make it available only when a trackpoint is selected.
8351 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8353 GtkWidget *insert_submenu = gtk_menu_new ();
8354 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8355 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8356 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8357 gtk_widget_show ( item );
8358 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8360 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8362 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8363 gtk_widget_show ( item );
8364 // Make it available only when a point is selected
8365 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8367 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8369 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8370 gtk_widget_show ( item );
8371 // Make it available only when a point is selected
8372 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8374 GtkWidget *delete_submenu;
8375 delete_submenu = gtk_menu_new ();
8376 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8377 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8378 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8379 gtk_widget_show ( item );
8380 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8382 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8383 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8384 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8385 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8386 gtk_widget_show ( item );
8387 // Make it available only when a point is selected
8388 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8390 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8392 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8393 gtk_widget_show ( item );
8395 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8396 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8397 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8398 gtk_widget_show ( item );
8400 GtkWidget *transform_submenu;
8401 transform_submenu = gtk_menu_new ();
8402 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8403 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8404 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8405 gtk_widget_show ( item );
8406 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8408 GtkWidget *dem_submenu;
8409 dem_submenu = gtk_menu_new ();
8410 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8411 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
8412 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8413 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8415 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8416 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8417 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8418 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8419 gtk_widget_show ( item );
8421 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8422 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8423 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8424 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8425 gtk_widget_show ( item );
8427 GtkWidget *smooth_submenu;
8428 smooth_submenu = gtk_menu_new ();
8429 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8430 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8431 gtk_widget_show ( item );
8432 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8434 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8435 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8436 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8437 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8438 gtk_widget_show ( item );
8440 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8441 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8442 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8443 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8444 gtk_widget_show ( item );
8446 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8447 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8449 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8450 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8451 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8452 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8453 gtk_widget_show ( item );
8455 // Routes don't have timestamps - so this is only available for tracks
8456 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8457 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8458 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8459 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8460 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8461 gtk_widget_show ( item );
8464 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8465 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8467 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8468 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8469 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8470 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8471 gtk_widget_show ( item );
8473 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8474 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8475 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8476 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8477 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8478 gtk_widget_show ( item );
8481 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8483 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8484 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8486 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8487 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
8488 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8489 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8490 gtk_widget_show ( item );
8493 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8494 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8496 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8497 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8498 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8499 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8500 gtk_widget_show ( item );
8502 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8503 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8505 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8506 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8507 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8508 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8509 gtk_widget_show ( item );
8511 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8512 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8513 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
8514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8515 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8516 gtk_widget_show ( item );
8519 // ATM can't upload a single waypoint but can do waypoints to a GPS
8520 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8521 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8522 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8523 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8524 gtk_widget_show ( item );
8525 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8527 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8528 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8529 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8530 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8531 gtk_widget_show ( item );
8535 // Only made available if a suitable program is installed
8536 if ( (have_astro_program || have_diary_program) &&
8537 (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
8538 GtkWidget *external_submenu;
8539 external_submenu = gtk_menu_new ();
8540 item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
8541 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
8542 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8543 gtk_widget_show ( item );
8544 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
8546 if ( have_diary_program ) {
8547 item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
8548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8550 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8551 gtk_widget_show ( item );
8554 if ( have_astro_program ) {
8555 item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
8556 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
8557 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8558 gtk_widget_show ( item );
8562 #ifdef VIK_CONFIG_GOOGLE
8563 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8565 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8566 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8567 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8568 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8569 gtk_widget_show ( item );
8573 // Some things aren't usable with routes
8574 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8575 #ifdef VIK_CONFIG_OPENSTREETMAP
8576 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8577 // Convert internal pointer into track
8578 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8579 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8580 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8581 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8582 gtk_widget_show ( item );
8585 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8586 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8587 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8588 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8589 gtk_widget_show ( item );
8591 /* ATM This function is only available via the layers panel, due to needing a vlp */
8593 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8594 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8595 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8597 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8598 gtk_widget_show ( item );
8602 #ifdef VIK_CONFIG_GEOTAG
8603 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8604 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8605 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8606 gtk_widget_show ( item );
8610 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8611 // Only show on viewport popmenu when a trackpoint is selected
8612 if ( ! vlp && l->current_tpl ) {
8614 item = gtk_menu_item_new ();
8615 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8616 gtk_widget_show ( item );
8618 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8619 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8621 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8622 gtk_widget_show ( item );
8626 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8627 GtkWidget *transform_submenu;
8628 transform_submenu = gtk_menu_new ();
8629 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8630 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8631 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8632 gtk_widget_show ( item );
8633 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8635 GtkWidget *dem_submenu;
8636 dem_submenu = gtk_menu_new ();
8637 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8638 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
8639 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8640 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8643 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8644 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8645 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8646 gtk_widget_show ( item );
8648 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8649 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8650 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8651 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8652 gtk_widget_show ( item );
8655 gtk_widget_show_all ( GTK_WIDGET(menu) );
8660 // TODO: Probably better to rework this track manipulation in viktrack.c
8661 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8664 if (!vtl->current_tpl)
8667 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8668 VikTrackpoint *tp_other = NULL;
8671 if (!vtl->current_tpl->prev)
8673 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8675 if (!vtl->current_tpl->next)
8677 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8680 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8683 VikTrackpoint *tp_new = vik_trackpoint_new();
8684 struct LatLon ll_current, ll_other;
8685 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8686 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8688 /* main positional interpolation */
8689 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8690 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8692 /* Now other properties that can be interpolated */
8693 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8695 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8696 /* Note here the division is applied to each part, then added
8697 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8698 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8699 tp_new->has_timestamp = TRUE;
8702 if (tp_current->speed != NAN && tp_other->speed != NAN)
8703 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8705 /* TODO - improve interpolation of course, as it may not be correct.
8706 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8707 [similar applies if value is in radians] */
8708 if (tp_current->course != NAN && tp_other->course != NAN)
8709 tp_new->course = (tp_current->course + tp_other->course)/2;
8711 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8713 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8714 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8716 // Otherwise try routes
8717 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8721 gint index = g_list_index ( trk->trackpoints, tp_current );
8725 // NB no recalculation of bounds since it is inserted between points
8726 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8731 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8737 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8741 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8743 if ( vtl->current_tpl )
8745 vtl->current_tpl = NULL;
8746 vtl->current_tp_track = NULL;
8747 vtl->current_tp_id = NULL;
8748 vik_layer_emit_update(VIK_LAYER(vtl));
8752 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8754 VikTrack *trk = vtl->current_tp_track;
8756 // Notional center of a track is simply an average of the bounding box extremities
8757 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8758 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er );
8759 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8762 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8764 g_assert ( vtl->tpwin != NULL );
8765 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8766 trw_layer_cancel_current_tp ( vtl, TRUE );
8768 if ( vtl->current_tpl == NULL )
8771 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8773 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8774 my_tpwin_set_tp ( vtl );
8776 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8778 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8780 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8784 trw_layer_trackpoint_selected_delete ( vtl, tr );
8786 if ( vtl->current_tpl )
8787 // Reset dialog with the available adjacent trackpoint
8788 my_tpwin_set_tp ( vtl );
8790 vik_layer_emit_update(VIK_LAYER(vtl));
8792 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8794 if ( vtl->current_tp_track ) {
8795 vtl->current_tpl = vtl->current_tpl->next;
8796 my_tpwin_set_tp ( vtl );
8798 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8800 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8802 if ( vtl->current_tp_track ) {
8803 vtl->current_tpl = vtl->current_tpl->prev;
8804 my_tpwin_set_tp ( vtl );
8806 vik_layer_emit_update(VIK_LAYER(vtl));
8808 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8810 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8811 vik_layer_emit_update(VIK_LAYER(vtl));
8813 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8814 vik_layer_emit_update(VIK_LAYER(vtl));
8818 * trw_layer_dialog_shift:
8819 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8821 * Try to reposition a dialog if it's over the specified coord
8822 * so to not obscure the item of interest
8824 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8826 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8828 // Attempt force dialog to be shown so we can find out where it is more reliably...
8829 while ( gtk_events_pending() )
8830 gtk_main_iteration ();
8832 // get parent window position & size
8833 gint win_pos_x, win_pos_y;
8834 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8836 gint win_size_x, win_size_y;
8837 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8839 // get own dialog size
8840 gint dia_size_x, dia_size_y;
8841 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8843 // get own dialog position
8844 gint dia_pos_x, dia_pos_y;
8845 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8847 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8848 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8850 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8852 gint vp_xx, vp_yy; // In viewport pixels
8853 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8855 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8859 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8861 // Transform Viewport pixels into absolute pixels
8862 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8863 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8865 // Is dialog over the point (to within an ^^ edge value)
8866 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8867 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8871 gint hh = vik_viewport_get_height ( vvp );
8873 // Consider the difference in viewport to the full window
8874 gint offset_y = dest_y;
8875 // Add difference between dialog and window sizes
8876 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8878 if ( vp_yy > hh/2 ) {
8879 // Point in bottom half, move window to top half
8880 gtk_window_move ( dialog, dia_pos_x, offset_y );
8883 // Point in top half, move dialog down
8884 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8888 // Shift left<->right
8889 gint ww = vik_viewport_get_width ( vvp );
8891 // Consider the difference in viewport to the full window
8892 gint offset_x = dest_x;
8893 // Add difference between dialog and window sizes
8894 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8896 if ( vp_xx > ww/2 ) {
8897 // Point on right, move window to left
8898 gtk_window_move ( dialog, offset_x, dia_pos_y );
8901 // Point on left, move right
8902 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8910 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8914 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8915 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8916 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8917 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8919 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8921 if ( vtl->current_tpl ) {
8922 // get tp pixel position
8923 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8925 // Shift up<->down to try not to obscure the trackpoint.
8926 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8930 if ( vtl->current_tpl )
8931 if ( vtl->current_tp_track )
8932 my_tpwin_set_tp ( vtl );
8933 /* set layer name and TP data */
8936 /***************************************************************************
8938 ***************************************************************************/
8940 /*** Utility data structures and functions ****/
8944 gint closest_x, closest_y;
8945 gboolean draw_images;
8946 gpointer *closest_wp_id;
8947 VikWaypoint *closest_wp;
8953 gint closest_x, closest_y;
8954 gpointer closest_track_id;
8955 VikTrackpoint *closest_tp;
8961 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8967 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8969 // If waypoint has an image then use the image size to select
8970 if ( params->draw_images && wp->image ) {
8971 gint slackx, slacky;
8972 slackx = wp->image_width / 2;
8973 slacky = wp->image_height / 2;
8975 if ( x <= params->x + slackx && x >= params->x - slackx
8976 && y <= params->y + slacky && y >= params->y - slacky ) {
8977 params->closest_wp_id = id;
8978 params->closest_wp = wp;
8979 params->closest_x = x;
8980 params->closest_y = y;
8983 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8984 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8985 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8987 params->closest_wp_id = id;
8988 params->closest_wp = wp;
8989 params->closest_x = x;
8990 params->closest_y = y;
8994 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8996 GList *tpl = t->trackpoints;
9002 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
9008 tp = VIK_TRACKPOINT(tpl->data);
9010 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
9012 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
9013 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
9014 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9016 params->closest_track_id = id;
9017 params->closest_tp = tp;
9018 params->closest_tpl = tpl;
9019 params->closest_x = x;
9020 params->closest_y = y;
9026 // ATM: Leave this as 'Track' only.
9027 // Not overly bothered about having a snap to route trackpoint capability
9028 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9030 TPSearchParams params;
9034 params.closest_track_id = NULL;
9035 params.closest_tp = NULL;
9036 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9037 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9038 return params.closest_tp;
9041 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9043 WPSearchParams params;
9047 params.draw_images = vtl->drawimages;
9048 params.closest_wp = NULL;
9049 params.closest_wp_id = NULL;
9050 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9051 return params.closest_wp;
9055 // Some forward declarations
9056 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
9057 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
9058 static void marker_end_move ( tool_ed_t *t );
9061 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
9065 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9067 // Here always allow snapping back to the original location
9068 // this is useful when one decides not to move the thing afterall
9069 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
9072 if ( event->state & GDK_CONTROL_MASK )
9074 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9076 new_coord = tp->coord;
9080 if ( event->state & GDK_SHIFT_MASK )
9082 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9084 new_coord = wp->coord;
9088 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9090 marker_moveto ( t, x, y );
9097 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
9099 if ( t->holding && event->button == 1 )
9101 // Prevent accidental (small) shifts when specific movement has not been requested
9102 // (as the click release has occurred within the click object detection area)
9107 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9110 if ( event->state & GDK_CONTROL_MASK )
9112 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9114 new_coord = tp->coord;
9118 if ( event->state & GDK_SHIFT_MASK )
9120 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9122 new_coord = wp->coord;
9125 marker_end_move ( t );
9127 // Determine if working on a waypoint or a trackpoint
9128 if ( t->is_waypoint ) {
9129 // Update waypoint position
9130 vtl->current_wp->coord = new_coord;
9131 trw_layer_calculate_bounds_waypoints ( vtl );
9132 // Reset waypoint pointer
9133 vtl->current_wp = NULL;
9134 vtl->current_wp_id = NULL;
9137 if ( vtl->current_tpl ) {
9138 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9140 if ( vtl->current_tp_track )
9141 vik_track_calculate_bounds ( vtl->current_tp_track );
9144 if ( vtl->current_tp_track )
9145 my_tpwin_set_tp ( vtl );
9146 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
9150 vik_layer_emit_update ( VIK_LAYER(vtl) );
9157 Returns true if a waypoint or track is found near the requested event position for this particular layer
9158 The item found is automatically selected
9159 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
9161 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
9163 if ( event->button != 1 )
9166 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9169 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9173 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
9175 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
9177 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
9178 WPSearchParams wp_params;
9179 wp_params.vvp = vvp;
9180 wp_params.x = event->x;
9181 wp_params.y = event->y;
9182 wp_params.draw_images = vtl->drawimages;
9183 wp_params.closest_wp_id = NULL;
9184 wp_params.closest_wp = NULL;
9186 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
9188 if ( wp_params.closest_wp ) {
9191 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
9193 // Too easy to move it so must be holding shift to start immediately moving it
9194 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
9195 if ( event->state & GDK_SHIFT_MASK ||
9196 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
9197 // Put into 'move buffer'
9198 // NB vvp & vw already set in tet
9199 tet->vtl = (gpointer)vtl;
9200 tet->is_waypoint = TRUE;
9202 marker_begin_move (tet, event->x, event->y);
9205 vtl->current_wp = wp_params.closest_wp;
9206 vtl->current_wp_id = wp_params.closest_wp_id;
9208 if ( event->type == GDK_2BUTTON_PRESS ) {
9209 if ( vtl->current_wp->image ) {
9210 menu_array_sublayer values;
9211 values[MA_VTL] = vtl;
9212 values[MA_MISC] = vtl->current_wp->image;
9213 trw_layer_show_picture ( values );
9217 vik_layer_emit_update ( VIK_LAYER(vtl) );
9223 // Used for both track and route lists
9224 TPSearchParams tp_params;
9225 tp_params.vvp = vvp;
9226 tp_params.x = event->x;
9227 tp_params.y = event->y;
9228 tp_params.closest_track_id = NULL;
9229 tp_params.closest_tp = NULL;
9230 tp_params.closest_tpl = NULL;
9231 tp_params.bbox = bbox;
9233 if (vtl->tracks_visible) {
9234 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9236 if ( tp_params.closest_tp ) {
9238 // Always select + highlight the track
9239 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9241 tet->is_waypoint = FALSE;
9243 // Select the Trackpoint
9244 // Can move it immediately when control held or it's the previously selected tp
9245 if ( event->state & GDK_CONTROL_MASK ||
9246 vtl->current_tpl == tp_params.closest_tpl ) {
9247 // Put into 'move buffer'
9248 // NB vvp & vw already set in tet
9249 tet->vtl = (gpointer)vtl;
9250 marker_begin_move (tet, event->x, event->y);
9253 vtl->current_tpl = tp_params.closest_tpl;
9254 vtl->current_tp_id = tp_params.closest_track_id;
9255 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9257 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9260 my_tpwin_set_tp ( vtl );
9262 vik_layer_emit_update ( VIK_LAYER(vtl) );
9267 // Try again for routes
9268 if (vtl->routes_visible) {
9269 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9271 if ( tp_params.closest_tp ) {
9273 // Always select + highlight the track
9274 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9276 tet->is_waypoint = FALSE;
9278 // Select the Trackpoint
9279 // Can move it immediately when control held or it's the previously selected tp
9280 if ( event->state & GDK_CONTROL_MASK ||
9281 vtl->current_tpl == tp_params.closest_tpl ) {
9282 // Put into 'move buffer'
9283 // NB vvp & vw already set in tet
9284 tet->vtl = (gpointer)vtl;
9285 marker_begin_move (tet, event->x, event->y);
9288 vtl->current_tpl = tp_params.closest_tpl;
9289 vtl->current_tp_id = tp_params.closest_track_id;
9290 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9292 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9295 my_tpwin_set_tp ( vtl );
9297 vik_layer_emit_update ( VIK_LAYER(vtl) );
9302 /* these aren't the droids you're looking for */
9303 vtl->current_wp = NULL;
9304 vtl->current_wp_id = NULL;
9305 trw_layer_cancel_current_tp ( vtl, FALSE );
9308 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9313 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9315 if ( event->button != 3 )
9318 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9321 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9324 /* Post menu for the currently selected item */
9326 /* See if a track is selected */
9327 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9328 if ( track && track->visible ) {
9330 if ( track->name ) {
9332 if ( vtl->track_right_click_menu )
9333 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9335 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9342 if ( track->is_route )
9343 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9345 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9347 if ( trkf && udataU.uuid ) {
9350 if ( track->is_route )
9351 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9353 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9355 trw_layer_sublayer_add_menu_items ( vtl,
9356 vtl->track_right_click_menu,
9358 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9364 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9370 /* See if a waypoint is selected */
9371 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9372 if ( waypoint && waypoint->visible ) {
9373 if ( waypoint->name ) {
9375 if ( vtl->wp_right_click_menu )
9376 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9378 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9381 udata.wp = waypoint;
9384 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9386 if ( wpf && udata.uuid ) {
9387 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9389 trw_layer_sublayer_add_menu_items ( vtl,
9390 vtl->wp_right_click_menu,
9392 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9397 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9406 /* background drawing hook, to be passed the viewport */
9407 static gboolean tool_sync_done = TRUE;
9409 static gboolean tool_sync(gpointer data)
9411 VikViewport *vvp = data;
9412 gdk_threads_enter();
9413 vik_viewport_sync(vvp);
9414 tool_sync_done = TRUE;
9415 gdk_threads_leave();
9419 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9422 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9423 gdk_gc_set_function ( t->gc, GDK_INVERT );
9424 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9425 vik_viewport_sync(t->vvp);
9431 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9433 VikViewport *vvp = t->vvp;
9434 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9435 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9440 if (tool_sync_done) {
9441 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9442 tool_sync_done = FALSE;
9446 static void marker_end_move ( tool_ed_t *t )
9448 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9449 g_object_unref ( t->gc );
9454 /*** Edit waypoint ****/
9456 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9458 tool_ed_t *t = g_new(tool_ed_t, 1);
9464 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9469 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9471 WPSearchParams params;
9472 tool_ed_t *t = data;
9473 VikViewport *vvp = t->vvp;
9475 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9482 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9485 if ( vtl->current_wp && vtl->current_wp->visible )
9487 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9489 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9491 if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX &&
9492 abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX )
9494 if ( event->button == 3 )
9495 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9497 marker_begin_move(t, event->x, event->y);
9504 params.x = event->x;
9505 params.y = event->y;
9506 params.draw_images = vtl->drawimages;
9507 params.closest_wp_id = NULL;
9508 params.closest_wp = NULL;
9509 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9510 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9512 if ( event->button == 3 )
9513 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9515 marker_begin_move(t, event->x, event->y);
9518 else if ( params.closest_wp )
9520 if ( event->button == 3 )
9521 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9523 vtl->waypoint_rightclick = FALSE;
9525 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9527 vtl->current_wp = params.closest_wp;
9528 vtl->current_wp_id = params.closest_wp_id;
9530 /* could make it so don't update if old WP is off screen and new is null but oh well */
9531 vik_layer_emit_update ( VIK_LAYER(vtl) );
9535 vtl->current_wp = NULL;
9536 vtl->current_wp_id = NULL;
9537 vtl->waypoint_rightclick = FALSE;
9538 vik_layer_emit_update ( VIK_LAYER(vtl) );
9542 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9544 tool_ed_t *t = data;
9545 VikViewport *vvp = t->vvp;
9547 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9552 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9555 if ( event->state & GDK_CONTROL_MASK )
9557 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9559 new_coord = tp->coord;
9563 if ( event->state & GDK_SHIFT_MASK )
9565 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9566 if ( wp && wp != vtl->current_wp )
9567 new_coord = wp->coord;
9572 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9574 marker_moveto ( t, x, y );
9581 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9583 tool_ed_t *t = data;
9584 VikViewport *vvp = t->vvp;
9586 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9589 if ( t->holding && event->button == 1 )
9592 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9595 if ( event->state & GDK_CONTROL_MASK )
9597 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9599 new_coord = tp->coord;
9603 if ( event->state & GDK_SHIFT_MASK )
9605 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9606 if ( wp && wp != vtl->current_wp )
9607 new_coord = wp->coord;
9610 marker_end_move ( t );
9612 vtl->current_wp->coord = new_coord;
9614 trw_layer_calculate_bounds_waypoints ( vtl );
9615 vik_layer_emit_update ( VIK_LAYER(vtl) );
9618 /* PUT IN RIGHT PLACE!!! */
9619 if ( event->button == 3 && vtl->waypoint_rightclick )
9621 if ( vtl->wp_right_click_menu )
9622 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9623 if ( vtl->current_wp ) {
9624 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9625 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 );
9626 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9628 vtl->waypoint_rightclick = FALSE;
9633 /*** New track ****/
9635 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9642 GdkDrawable *drawable;
9648 * Draw specified pixmap
9650 static gboolean draw_sync ( gpointer data )
9652 draw_sync_t *ds = (draw_sync_t*) data;
9653 // Sometimes don't want to draw
9654 // normally because another update has taken precedent such as panning the display
9655 // which means this pixmap is no longer valid
9656 if ( ds->vtl->draw_sync_do ) {
9657 gdk_threads_enter();
9658 gdk_draw_drawable (ds->drawable,
9661 0, 0, 0, 0, -1, -1);
9662 ds->vtl->draw_sync_done = TRUE;
9663 gdk_threads_leave();
9669 static gchar* distance_string (gdouble distance)
9673 /* draw label with distance */
9674 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9675 switch (dist_units) {
9676 case VIK_UNITS_DISTANCE_MILES:
9677 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9678 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9679 } else if (distance < 1609.4) {
9680 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9682 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9685 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9686 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9687 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9688 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9689 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9691 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9695 // VIK_UNITS_DISTANCE_KILOMETRES
9696 if (distance >= 1000 && distance < 100000) {
9697 g_sprintf(str, "%3.2f km", distance/1000.0);
9698 } else if (distance < 1000) {
9699 g_sprintf(str, "%d m", (int)distance);
9701 g_sprintf(str, "%d km", (int)distance/1000);
9705 return g_strdup (str);
9709 * Actually set the message in statusbar
9711 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9713 // Only show elevation data when track has some elevation properties
9714 gchar str_gain_loss[64];
9715 str_gain_loss[0] = '\0';
9716 gchar str_last_step[64];
9717 str_last_step[0] = '\0';
9718 gchar *str_total = distance_string (distance);
9720 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9721 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9722 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9724 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9727 if ( last_step > 0 ) {
9728 gchar *tmp = distance_string (last_step);
9729 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9733 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9735 // Write with full gain/loss information
9736 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9737 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9739 g_free ( str_total );
9743 * Figure out what information should be set in the statusbar and then write it
9745 static void update_statusbar ( VikTrwLayer *vtl )
9747 // Get elevation data
9748 gdouble elev_gain, elev_loss;
9749 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9751 /* Find out actual distance of current track */
9752 gdouble distance = vik_track_get_length (vtl->current_track);
9754 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9758 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9760 /* if we haven't sync'ed yet, we don't have time to do more. */
9761 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9762 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9764 static GdkPixmap *pixmap = NULL;
9766 // Need to check in case window has been resized
9767 w1 = vik_viewport_get_width(vvp);
9768 h1 = vik_viewport_get_height(vvp);
9770 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9772 gdk_drawable_get_size (pixmap, &w2, &h2);
9773 if (w1 != w2 || h1 != h2) {
9774 g_object_unref ( G_OBJECT ( pixmap ) );
9775 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9778 // Reset to background
9779 gdk_draw_drawable (pixmap,
9780 vtl->current_track_newpoint_gc,
9781 vik_viewport_get_pixmap(vvp),
9782 0, 0, 0, 0, -1, -1);
9784 draw_sync_t *passalong;
9787 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9789 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9790 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9791 // thus when we come to reset to the background it would include what we have already drawn!!
9792 gdk_draw_line ( pixmap,
9793 vtl->current_track_newpoint_gc,
9794 x1, y1, event->x, event->y );
9795 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9797 /* Find out actual distance of current track */
9798 gdouble distance = vik_track_get_length (vtl->current_track);
9800 // Now add distance to where the pointer is //
9803 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9804 vik_coord_to_latlon ( &coord, &ll );
9805 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9806 distance = distance + last_step;
9808 // Get elevation data
9809 gdouble elev_gain, elev_loss;
9810 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9812 // Adjust elevation data (if available) for the current pointer position
9814 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9815 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9816 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9817 // Adjust elevation of last track point
9818 if ( elev_new > last_tpt->altitude )
9820 elev_gain += elev_new - last_tpt->altitude;
9823 elev_loss += last_tpt->altitude - elev_new;
9828 // Display of the distance 'tooltip' during track creation is controlled by a preference
9830 if ( a_vik_get_create_track_tooltip() ) {
9832 gchar *str = distance_string (distance);
9834 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9835 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9836 pango_layout_set_text (pl, str, -1);
9838 pango_layout_get_pixel_size ( pl, &wd, &hd );
9841 // offset from cursor a bit depending on font size
9845 // Create a background block to make the text easier to read over the background map
9846 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9847 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9848 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9850 g_object_unref ( G_OBJECT ( pl ) );
9851 g_object_unref ( G_OBJECT ( background_block_gc ) );
9855 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9856 passalong->vtl = vtl;
9857 passalong->pixmap = pixmap;
9858 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9859 passalong->gc = vtl->current_track_newpoint_gc;
9863 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9865 // Update statusbar with full gain/loss information
9866 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9868 // draw pixmap when we have time to
9869 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9870 vtl->draw_sync_done = FALSE;
9871 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9873 return VIK_LAYER_TOOL_ACK;
9876 // NB vtl->current_track must be valid
9877 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9880 if ( vtl->current_track->trackpoints ) {
9881 // TODO rework this...
9882 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9883 GList *last = g_list_last(vtl->current_track->trackpoints);
9884 g_free ( last->data );
9885 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9887 vik_track_calculate_bounds ( vtl->current_track );
9891 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9893 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9894 // Bin track if only one point as it's not very useful
9895 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9896 if ( vtl->current_track->is_route )
9897 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9899 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9901 vtl->current_track = NULL;
9902 vik_layer_emit_update ( VIK_LAYER(vtl) );
9904 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9905 undo_trackpoint_add ( vtl );
9906 update_statusbar ( vtl );
9907 vik_layer_emit_update ( VIK_LAYER(vtl) );
9914 * Common function to handle trackpoint button requests on either a route or a track
9915 * . enables adding a point via normal click
9916 * . enables removal of last point via right click
9917 * . finishing of the track or route via double clicking
9919 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9923 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9926 if ( event->button == 2 ) {
9927 // As the display is panning, the new track pixmap is now invalid so don't draw it
9928 // otherwise this drawing done results in flickering back to an old image
9929 vtl->draw_sync_do = FALSE;
9933 if ( event->button == 3 )
9935 if ( !vtl->current_track )
9937 undo_trackpoint_add ( vtl );
9938 update_statusbar ( vtl );
9939 vik_layer_emit_update ( VIK_LAYER(vtl) );
9943 if ( event->type == GDK_2BUTTON_PRESS )
9945 /* subtract last (duplicate from double click) tp then end */
9946 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9948 /* undo last, then end */
9949 undo_trackpoint_add ( vtl );
9950 vtl->current_track = NULL;
9952 vik_layer_emit_update ( VIK_LAYER(vtl) );
9956 tp = vik_trackpoint_new();
9957 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9959 /* snap to other TP */
9960 if ( event->state & GDK_CONTROL_MASK )
9962 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9964 tp->coord = other_tp->coord;
9967 tp->newsegment = FALSE;
9968 tp->has_timestamp = FALSE;
9971 if ( vtl->current_track ) {
9972 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9973 /* Auto attempt to get elevation from DEM data (if it's available) */
9974 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9977 vtl->ct_x1 = vtl->ct_x2;
9978 vtl->ct_y1 = vtl->ct_y2;
9979 vtl->ct_x2 = event->x;
9980 vtl->ct_y2 = event->y;
9982 vik_layer_emit_update ( VIK_LAYER(vtl) );
9986 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9988 // if we were running the route finder, cancel it
9989 vtl->route_finder_started = FALSE;
9991 // ----------------------------------------------------- if current is a route - switch to new track
9992 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9994 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9995 if ( a_vik_get_ask_for_create_track_name() ) {
9996 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
10000 new_track_create_common ( vtl, name );
10003 return tool_new_track_or_route_click ( vtl, event, vvp );
10006 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10008 if ( event->button == 2 ) {
10009 // Pan moving ended - enable potential point drawing again
10010 vtl->draw_sync_do = TRUE;
10011 vtl->draw_sync_done = TRUE;
10015 /*** New route ****/
10017 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
10022 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10024 // if we were running the route finder, cancel it
10025 vtl->route_finder_started = FALSE;
10027 // -------------------------- if current is a track - switch to new route,
10028 if ( event->button == 1 && ( ! vtl->current_track ||
10029 (vtl->current_track && !vtl->current_track->is_route ) ) )
10031 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
10032 if ( a_vik_get_ask_for_create_track_name() ) {
10033 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
10037 new_route_create_common ( vtl, name );
10040 return tool_new_track_or_route_click ( vtl, event, vvp );
10043 /*** New waypoint ****/
10045 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
10050 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10053 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10055 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
10056 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
10057 trw_layer_calculate_bounds_waypoints ( vtl );
10058 vik_layer_emit_update ( VIK_LAYER(vtl) );
10064 /*** Edit trackpoint ****/
10066 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
10068 tool_ed_t *t = g_new(tool_ed_t, 1);
10070 t->holding = FALSE;
10074 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
10080 * tool_edit_trackpoint_click:
10082 * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint
10083 * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information.
10084 * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point
10085 * then initiate the move operation to drag the point to a new destination.
10086 * NB The current trackpoint will get reset elsewhere.
10088 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10090 tool_ed_t *t = data;
10091 VikViewport *vvp = t->vvp;
10092 TPSearchParams params;
10094 params.x = event->x;
10095 params.y = event->y;
10096 params.closest_track_id = NULL;
10097 params.closest_tp = NULL;
10098 params.closest_tpl = NULL;
10099 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
10101 if ( event->button != 1 )
10104 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10107 if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) )
10110 if ( vtl->current_tpl )
10112 /* 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.) */
10113 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
10114 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
10116 current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id));
10121 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
10123 if ( current_tr->visible &&
10124 abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX &&
10125 abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) {
10126 marker_begin_move ( t, event->x, event->y );
10132 if ( vtl->tracks_visible )
10133 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
10135 if ( params.closest_tp )
10137 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
10138 vtl->current_tpl = params.closest_tpl;
10139 vtl->current_tp_id = params.closest_track_id;
10140 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
10141 trw_layer_tpwin_init ( vtl );
10142 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10143 vik_layer_emit_update ( VIK_LAYER(vtl) );
10147 if ( vtl->routes_visible )
10148 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
10150 if ( params.closest_tp )
10152 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
10153 vtl->current_tpl = params.closest_tpl;
10154 vtl->current_tp_id = params.closest_track_id;
10155 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
10156 trw_layer_tpwin_init ( vtl );
10157 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10158 vik_layer_emit_update ( VIK_LAYER(vtl) );
10162 /* these aren't the droids you're looking for */
10166 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
10168 tool_ed_t *t = data;
10169 VikViewport *vvp = t->vvp;
10171 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10176 VikCoord new_coord;
10177 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10180 if ( event->state & GDK_CONTROL_MASK )
10182 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10183 if ( tp && tp != vtl->current_tpl->data )
10184 new_coord = tp->coord;
10186 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10189 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
10190 marker_moveto ( t, x, y );
10198 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10200 tool_ed_t *t = data;
10201 VikViewport *vvp = t->vvp;
10203 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10205 if ( event->button != 1)
10208 if ( t->holding ) {
10209 VikCoord new_coord;
10210 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10213 if ( event->state & GDK_CONTROL_MASK )
10215 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10216 if ( tp && tp != vtl->current_tpl->data )
10217 new_coord = tp->coord;
10220 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10221 if ( vtl->current_tp_track )
10222 vik_track_calculate_bounds ( vtl->current_tp_track );
10224 marker_end_move ( t );
10226 /* diff dist is diff from orig */
10228 my_tpwin_set_tp ( vtl );
10230 vik_layer_emit_update ( VIK_LAYER(vtl) );
10237 /*** Extended Route Finder ***/
10239 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10244 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10247 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10249 g_free ( new_end );
10250 vik_layer_emit_update ( VIK_LAYER(vtl) );
10252 /* remove last ' to:...' */
10253 if ( vtl->current_track->comment ) {
10254 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10255 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10256 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10257 last_to - vtl->current_track->comment - 1);
10258 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10265 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10268 if ( !vtl ) return FALSE;
10269 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10270 if ( event->button == 3 && vtl->current_track ) {
10271 tool_extended_route_finder_undo ( vtl );
10273 else if ( event->button == 2 ) {
10274 vtl->draw_sync_do = FALSE;
10277 // if we started the track but via undo deleted all the track points, begin again
10278 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10279 return tool_new_track_or_route_click ( vtl, event, vvp );
10281 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10282 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10283 struct LatLon start, end;
10285 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10286 vik_coord_to_latlon ( &(tp_start->coord), &start );
10287 vik_coord_to_latlon ( &(tmp), &end );
10289 vtl->route_finder_started = TRUE;
10290 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10292 // update UI to let user know what's going on
10293 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10294 VikRoutingEngine *engine = vik_routing_default_engine ( );
10296 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10299 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10300 vik_routing_engine_get_label ( engine ),
10301 start.lat, start.lon, end.lat, end.lon );
10302 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10304 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10307 /* Give GTK a change to display the new status bar before querying the web */
10308 while ( gtk_events_pending ( ) )
10309 gtk_main_iteration ( );
10311 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10313 /* Update UI to say we're done */
10314 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10315 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10316 vik_routing_engine_get_label ( engine ),
10317 start.lat, start.lon, end.lat, end.lon )
10318 : g_strdup_printf ( _("Error getting route from %s."),
10319 vik_routing_engine_get_label ( engine ) );
10320 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10323 vik_layer_emit_update ( VIK_LAYER(vtl) );
10325 vtl->current_track = NULL;
10327 // create a new route where we will add the planned route to
10328 gboolean ret = tool_new_route_click( vtl, event, vvp );
10330 vtl->route_finder_started = TRUE;
10337 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10339 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10340 vtl->route_finder_started = FALSE;
10341 vtl->current_track = NULL;
10342 vik_layer_emit_update ( VIK_LAYER(vtl) );
10344 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10345 tool_extended_route_finder_undo ( vtl );
10352 /*** Show picture ****/
10354 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10359 /* Params are: vvp, event, last match found or NULL */
10360 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10362 if ( wp->image && wp->visible )
10364 gint x, y, slackx, slacky;
10365 GdkEventButton *event = (GdkEventButton *) params[1];
10367 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10368 slackx = wp->image_width / 2;
10369 slacky = wp->image_height / 2;
10370 if ( x <= event->x + slackx && x >= event->x - slackx
10371 && y <= event->y + slacky && y >= event->y - slacky )
10373 params[2] = wp->image; /* we've found a match. however continue searching
10374 * since we want to find the last match -- that
10375 * is, the match that was drawn last. */
10380 static void trw_layer_show_picture ( menu_array_sublayer values )
10382 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10384 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10385 #else /* WINDOWS */
10386 GError *err = NULL;
10387 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10388 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10389 g_free ( quoted_file );
10390 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10392 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
10393 g_error_free ( err );
10396 #endif /* WINDOWS */
10399 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10401 gpointer params[3] = { vvp, event, NULL };
10402 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10404 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10407 static menu_array_sublayer values;
10408 values[MA_VTL] = vtl;
10409 values[MA_MISC] = params[2];
10410 trw_layer_show_picture ( values );
10411 return TRUE; /* found a match */
10414 return FALSE; /* go through other layers, searching for a match */
10417 /***************************************************************************
10419 ***************************************************************************/
10422 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10424 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10425 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10428 /* Structure for thumbnail creating data used in the background thread */
10430 VikTrwLayer *vtl; // Layer needed for redrawing
10431 GSList *pics; // Image list
10432 } thumbnail_create_thread_data;
10434 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10436 guint total = g_slist_length(tctd->pics), done = 0;
10437 while ( tctd->pics )
10439 a_thumbnails_create ( (gchar *) tctd->pics->data );
10440 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10442 return -1; /* Abort thread */
10444 tctd->pics = tctd->pics->next;
10447 // Redraw to show the thumbnails as they are now created
10448 if ( IS_VIK_LAYER(tctd->vtl) )
10449 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10454 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10456 while ( tctd->pics )
10458 g_free ( tctd->pics->data );
10459 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10464 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10466 if ( ! vtl->has_verified_thumbnails )
10468 GSList *pics = NULL;
10469 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10472 gint len = g_slist_length ( pics );
10473 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10474 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10477 a_background_thread ( BACKGROUND_POOL_LOCAL,
10478 VIK_GTK_WINDOW_FROM_LAYER(vtl),
10480 (vik_thr_func) create_thumbnails_thread,
10482 (vik_thr_free_func) thumbnail_create_thread_free,
10490 static const gchar* my_track_colors ( gint ii )
10492 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10504 // Fast and reliable way of returning a colour
10505 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10508 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10510 GHashTableIter iter;
10511 gpointer key, value;
10515 g_hash_table_iter_init ( &iter, vtl->tracks );
10517 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10519 // Tracks get a random spread of colours if not already assigned
10520 if ( ! VIK_TRACK(value)->has_color ) {
10521 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10522 VIK_TRACK(value)->color = vtl->track_color;
10524 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10526 VIK_TRACK(value)->has_color = TRUE;
10529 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10532 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10538 g_hash_table_iter_init ( &iter, vtl->routes );
10540 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10542 // Routes get an intermix of reds
10543 if ( ! VIK_TRACK(value)->has_color ) {
10545 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10547 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10548 VIK_TRACK(value)->has_color = TRUE;
10551 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10558 * (Re)Calculate the bounds of the waypoints in this layer,
10559 * This should be called whenever waypoints are changed
10561 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10563 struct LatLon topleft = { 0.0, 0.0 };
10564 struct LatLon bottomright = { 0.0, 0.0 };
10567 GHashTableIter iter;
10568 gpointer key, value;
10570 g_hash_table_iter_init ( &iter, vtl->waypoints );
10572 // Set bounds to first point
10573 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10574 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10575 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10578 // Ensure there is another point...
10579 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10581 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10583 // See if this point increases the bounds.
10584 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10586 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10587 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10588 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10589 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10593 vtl->waypoints_bbox.north = topleft.lat;
10594 vtl->waypoints_bbox.east = bottomright.lon;
10595 vtl->waypoints_bbox.south = bottomright.lat;
10596 vtl->waypoints_bbox.west = topleft.lon;
10599 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10601 vik_track_calculate_bounds ( trk );
10604 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10606 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10607 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10610 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10612 if ( ! VIK_LAYER(vtl)->vt )
10615 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10616 if ( g_hash_table_size (vtl->tracks) > 1 )
10617 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10619 if ( g_hash_table_size (vtl->routes) > 1 )
10620 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10622 if ( g_hash_table_size (vtl->waypoints) > 1 )
10623 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10627 * Get the earliest timestamp available from all tracks
10629 static time_t trw_layer_get_timestamp_tracks ( VikTrwLayer *vtl )
10631 time_t timestamp = 0;
10632 GList *gl = g_hash_table_get_values ( vtl->tracks );
10633 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10634 gl = g_list_first ( gl );
10637 // Only need to check the first track as they have been sorted by time
10638 VikTrack *trk = (VikTrack*)gl->data;
10639 // Assume trackpoints already sorted by time
10640 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10641 if ( tpt && tpt->has_timestamp ) {
10642 timestamp = tpt->timestamp;
10644 g_list_free ( gl );
10650 * Get the earliest timestamp available from all waypoints
10652 static time_t trw_layer_get_timestamp_waypoints ( VikTrwLayer *vtl )
10654 time_t timestamp = 0;
10655 GList *gl = g_hash_table_get_values ( vtl->waypoints );
10657 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10658 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10659 if ( wpt->has_timestamp ) {
10660 // When timestamp not set yet - use the first value encountered
10661 if ( timestamp == 0 )
10662 timestamp = wpt->timestamp;
10663 else if ( timestamp > wpt->timestamp )
10664 timestamp = wpt->timestamp;
10667 g_list_free ( gl );
10673 * Get the earliest timestamp available for this layer
10675 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl )
10677 time_t timestamp_tracks = trw_layer_get_timestamp_tracks ( vtl );
10678 time_t timestamp_waypoints = trw_layer_get_timestamp_waypoints ( vtl );
10679 // NB routes don't have timestamps - hence they are not considered
10681 if ( !timestamp_tracks && !timestamp_waypoints ) {
10682 // Fallback to get time from the metadata when no other timestamps available
10684 if ( vtl->metadata && vtl->metadata->timestamp && g_time_val_from_iso8601 ( vtl->metadata->timestamp, >v ) )
10687 if ( timestamp_tracks && !timestamp_waypoints )
10688 return timestamp_tracks;
10689 if ( timestamp_tracks && timestamp_waypoints && (timestamp_tracks < timestamp_waypoints) )
10690 return timestamp_tracks;
10691 return timestamp_waypoints;
10694 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10696 if ( VIK_LAYER(vtl)->realized )
10697 trw_layer_verify_thumbnails ( vtl, vvp );
10698 trw_layer_track_alloc_colors ( vtl );
10700 trw_layer_calculate_bounds_waypoints ( vtl );
10701 trw_layer_calculate_bounds_tracks ( vtl );
10703 // Apply treeview sort after loading all the tracks for this layer
10704 // (rather than sorted insert on each individual track additional)
10705 // and after subsequent changes to the properties as the specified order may have changed.
10706 // since the sorting of a treeview section is now very quick
10707 // NB sorting is also performed after every name change as well to maintain the list order
10708 trw_layer_sort_all ( vtl );
10710 // Setting metadata time if not otherwise set
10711 if ( vtl->metadata ) {
10713 gboolean need_to_set_time = TRUE;
10714 if ( vtl->metadata->timestamp ) {
10715 need_to_set_time = FALSE;
10716 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10717 need_to_set_time = TRUE;
10720 if ( need_to_set_time ) {
10721 GTimeVal timestamp;
10722 timestamp.tv_usec = 0;
10723 timestamp.tv_sec = trw_layer_get_timestamp ( vtl );
10725 // No time found - so use 'now' for the metadata time
10726 if ( timestamp.tv_sec == 0 ) {
10727 g_get_current_time ( ×tamp );
10730 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10735 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10737 return vtl->coord_mode;
10741 * Uniquify the whole layer
10742 * Also requires the layers panel as the names shown there need updating too
10743 * Returns whether the operation was successful or not
10745 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10747 if ( vtl && vlp ) {
10748 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10749 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10750 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10756 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10758 vik_coord_convert ( &(wp->coord), *dest_mode );
10761 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10763 vik_track_convert ( tr, *dest_mode );
10766 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10768 if ( vtl->coord_mode != dest_mode )
10770 vtl->coord_mode = dest_mode;
10771 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10772 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10773 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10777 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10779 vtl->menu_selection = selection;
10782 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10784 return (vtl->menu_selection);
10787 /* ----------- Downloading maps along tracks --------------- */
10789 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10791 /* TODO: calculating based on current size of viewport */
10792 const gdouble w_at_zoom_0_125 = 0.0013;
10793 const gdouble h_at_zoom_0_125 = 0.0011;
10794 gdouble zoom_factor = zoom_level/0.125;
10796 wh->lat = h_at_zoom_0_125 * zoom_factor;
10797 wh->lon = w_at_zoom_0_125 * zoom_factor;
10799 return 0; /* all OK */
10802 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10804 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10805 (dist->lat >= ABS(to->north_south - from->north_south)))
10808 VikCoord *coord = g_malloc(sizeof(VikCoord));
10809 coord->mode = VIK_COORD_LATLON;
10811 if (ABS(gradient) < 1) {
10812 if (from->east_west > to->east_west)
10813 coord->east_west = from->east_west - dist->lon;
10815 coord->east_west = from->east_west + dist->lon;
10816 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10818 if (from->north_south > to->north_south)
10819 coord->north_south = from->north_south - dist->lat;
10821 coord->north_south = from->north_south + dist->lat;
10822 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10828 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10830 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10831 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10833 VikCoord *next = from;
10835 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10837 list = g_list_prepend(list, next);
10843 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10845 typedef struct _Rect {
10850 #define GLRECT(iter) ((Rect *)((iter)->data))
10853 GList *rects_to_download = NULL;
10856 if (get_download_area_width(vvp, zoom_level, &wh))
10859 GList *iter = tr->trackpoints;
10863 gboolean new_map = TRUE;
10864 VikCoord *cur_coord, tl, br;
10867 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10869 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10870 rect = g_malloc(sizeof(Rect));
10873 rect->center = *cur_coord;
10874 rects_to_download = g_list_prepend(rects_to_download, rect);
10879 gboolean found = FALSE;
10880 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10881 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10892 GList *fillins = NULL;
10893 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10894 /* seems that ATM the function get_next_coord works only for LATLON */
10895 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10896 /* fill-ins for far apart points */
10897 GList *cur_rect, *next_rect;
10898 for (cur_rect = rects_to_download;
10899 (next_rect = cur_rect->next) != NULL;
10900 cur_rect = cur_rect->next) {
10901 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10902 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10903 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10907 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10910 GList *fiter = fillins;
10912 cur_coord = (VikCoord *)(fiter->data);
10913 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10914 rect = g_malloc(sizeof(Rect));
10917 rect->center = *cur_coord;
10918 rects_to_download = g_list_prepend(rects_to_download, rect);
10919 fiter = fiter->next;
10923 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10924 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10928 for (iter = fillins; iter; iter = iter->next)
10929 g_free(iter->data);
10930 g_list_free(fillins);
10932 if (rects_to_download) {
10933 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10934 g_free(rect_iter->data);
10935 g_list_free(rects_to_download);
10939 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10943 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10944 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10945 gint selected_zoom, default_zoom;
10947 VikTrwLayer *vtl = values[MA_VTL];
10948 VikLayersPanel *vlp = values[MA_VLP];
10950 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10951 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10953 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10957 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10959 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10960 int num_maps = g_list_length(vmls);
10963 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10967 // Convert from list of vmls to list of names. Allowing the user to select one of them
10968 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10969 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10971 gchar **np = map_names;
10972 VikMapsLayer **lp = map_layers;
10974 for (i = 0; i < num_maps; i++) {
10975 vml = (VikMapsLayer *)(vmls->data);
10977 *np++ = vik_maps_layer_get_map_label(vml);
10980 // Mark end of the array lists
10984 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10985 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10986 if (cur_zoom == zoom_vals[default_zoom])
10989 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10991 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10994 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10997 for (i = 0; i < num_maps; i++)
10998 g_free(map_names[i]);
11000 g_free(map_layers);
11006 /**** lowest waypoint number calculation ***/
11007 static gint highest_wp_number_name_to_number(const gchar *name) {
11008 if ( strlen(name) == 3 ) {
11009 int n = atoi(name);
11010 if ( n < 100 && name[0] != '0' )
11012 if ( n < 10 && name[0] != '0' )
11020 static void highest_wp_number_reset(VikTrwLayer *vtl)
11022 vtl->highest_wp_number = -1;
11025 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
11027 /* if is bigger that top, add it */
11028 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
11029 if ( new_wp_num > vtl->highest_wp_number )
11030 vtl->highest_wp_number = new_wp_num;
11033 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
11035 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
11036 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
11037 if ( vtl->highest_wp_number == old_wp_num ) {
11039 vtl->highest_wp_number--;
11041 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11042 /* search down until we find something that *does* exist */
11044 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
11045 vtl->highest_wp_number--;
11046 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11051 /* get lowest unused number */
11052 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
11055 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
11057 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
11058 return g_strdup(buf);
11062 * trw_layer_create_track_list_both:
11064 * Create the latest list of tracks and routes
11066 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
11068 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
11069 GList *tracks = NULL;
11070 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
11071 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
11073 return vik_trw_layer_build_track_list_t ( vtl, tracks );
11076 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
11078 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11080 gchar *title = NULL;
11081 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
11082 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
11084 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
11086 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
11090 static void trw_layer_track_list_dialog ( menu_array_layer values )
11092 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11094 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
11095 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
11099 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
11101 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11103 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
11104 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );