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 "vikexttools.h"
63 #include "vikexttool_datasources.h"
67 #include "vikrouting.h"
69 #include "icons/icons.h"
83 #include <gdk/gdkkeysyms.h>
85 #include <glib/gstdio.h>
86 #include <glib/gi18n.h>
88 #define VIK_TRW_LAYER_TRACK_GC 6
89 #define VIK_TRW_LAYER_TRACK_GCS 10
90 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
91 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
92 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
93 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
94 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
95 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
97 #define DRAWMODE_BY_TRACK 0
98 #define DRAWMODE_BY_SPEED 1
99 #define DRAWMODE_ALL_SAME_COLOR 2
100 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
101 // as we are (re)calculating the colour for every point
106 /* this is how it knows when you click if you are clicking close to a trackpoint. */
107 #define TRACKPOINT_SIZE_APPROX 5
108 #define WAYPOINT_SIZE_APPROX 5
110 #define MIN_STOP_LENGTH 15
111 #define MAX_STOP_LENGTH 86400
112 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
113 /* this is multiplied by user-inputted value from 1-100. */
115 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
117 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
119 FS_XX_SMALL = 0, // 'xx-small'
122 FS_MEDIUM, // DEFAULT
129 struct _VikTrwLayer {
132 GHashTable *tracks_iters;
134 GHashTable *routes_iters;
135 GHashTable *waypoints_iters;
136 GHashTable *waypoints;
137 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
138 gboolean tracks_visible, routes_visible, waypoints_visible;
139 LatLonBBox waypoints_bbox;
141 gboolean track_draw_labels;
144 guint8 drawpoints_size;
145 guint8 drawelevation;
146 guint8 elevation_factor;
150 guint8 drawdirections;
151 guint8 drawdirections_size;
152 guint8 line_thickness;
153 guint8 bg_line_thickness;
154 vik_layer_sort_order_t track_sort_order;
157 VikTRWMetadata *metadata;
159 PangoLayout *tracklabellayout;
160 font_size_t track_font_size;
161 gchar *track_fsize_str;
165 gboolean wp_draw_symbols;
166 font_size_t wp_font_size;
168 vik_layer_sort_order_t wp_sort_order;
170 gdouble track_draw_speed_factor;
172 GdkGC *track_1color_gc;
173 GdkColor track_color;
174 GdkGC *current_track_gc;
175 // Separate GC for a track's potential new point as drawn via separate method
176 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
177 GdkGC *current_track_newpoint_gc;
178 GdkGC *track_bg_gc; GdkColor track_bg_color;
179 GdkGC *waypoint_gc; GdkColor waypoint_color;
180 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
181 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
183 GdkFont *waypoint_font;
184 VikTrack *current_track; // ATM shared between new tracks and new routes
185 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
186 gboolean draw_sync_done;
187 gboolean draw_sync_do;
189 VikCoordMode coord_mode;
191 /* wp editing tool */
192 VikWaypoint *current_wp;
193 gpointer current_wp_id;
195 gboolean waypoint_rightclick;
197 /* track editing tool */
199 VikTrack *current_tp_track;
200 gpointer current_tp_id;
201 VikTrwLayerTpwin *tpwin;
203 /* track editing tool -- more specifically, moving tps */
206 /* route finder tool */
207 gboolean route_finder_started;
208 gboolean route_finder_check_added_track;
209 VikTrack *route_finder_added_track;
210 gboolean route_finder_append;
217 guint16 image_cache_size;
219 /* for waypoint text */
220 PangoLayout *wplabellayout;
222 gboolean has_verified_thumbnails;
224 GtkMenu *wp_right_click_menu;
225 GtkMenu *track_right_click_menu;
228 VikStdLayerMenuItem menu_selection;
230 gint highest_wp_number;
233 GtkWidget *tracks_analysis_dialog;
236 /* A caached waypoint image. */
239 gchar *image; /* filename */
242 struct DrawingParams {
247 guint16 width, height;
248 gdouble cc; // Cosine factor in track directions
249 gdouble ss; // Sine factor in track directions
250 const VikCoord *center;
251 gboolean one_zone, lat_lon;
252 gdouble ce1, ce2, cn1, cn2;
257 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
262 MA_SUBTYPE, // OR END for Layer only
271 typedef gpointer menu_array_layer[2];
272 typedef gpointer menu_array_sublayer[MA_LAST];
274 static void trw_layer_delete_item ( menu_array_sublayer values );
275 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
276 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
278 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
279 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
281 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
282 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
284 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
285 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
287 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
288 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
289 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
290 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
291 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
292 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
293 static void trw_layer_goto_track_center ( menu_array_sublayer values );
294 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
295 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
296 static void trw_layer_merge_with_other ( menu_array_sublayer values );
297 static void trw_layer_append_track ( menu_array_sublayer values );
298 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
299 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
300 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
301 static void trw_layer_split_segments ( menu_array_sublayer values );
302 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
303 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
304 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
305 static void trw_layer_reverse ( menu_array_sublayer values );
306 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
307 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
308 static void trw_layer_show_picture ( menu_array_sublayer values );
309 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
311 static void trw_layer_centerize ( menu_array_layer values );
312 static void trw_layer_auto_view ( menu_array_layer values );
313 static void trw_layer_goto_wp ( menu_array_layer values );
314 static void trw_layer_new_wp ( menu_array_layer values );
315 static void trw_layer_new_track ( menu_array_layer values );
316 static void trw_layer_new_route ( menu_array_layer values );
317 static void trw_layer_finish_track ( menu_array_layer values );
318 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
319 static void trw_layer_auto_tracks_view ( menu_array_layer values );
320 static void trw_layer_delete_all_tracks ( menu_array_layer values );
321 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
322 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
323 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
324 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
325 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
326 #ifdef VIK_CONFIG_GEOTAG
327 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
328 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
329 static void trw_layer_geotagging_track ( menu_array_sublayer values );
330 static void trw_layer_geotagging ( menu_array_layer values );
332 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
333 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
334 static void trw_layer_acquire_url_cb ( menu_array_layer values );
335 #ifdef VIK_CONFIG_OPENSTREETMAP
336 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
337 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
339 #ifdef VIK_CONFIG_GEOCACHES
340 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
342 #ifdef VIK_CONFIG_GEOTAG
343 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
345 static void trw_layer_acquire_file_cb ( menu_array_layer values );
346 static void trw_layer_gps_upload ( menu_array_layer values );
348 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
349 static void trw_layer_track_list_dialog ( menu_array_layer values );
350 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
352 // Specific route versions:
353 // Most track handling functions can handle operating on the route list
354 // However these ones are easier in separate functions
355 static void trw_layer_auto_routes_view ( menu_array_layer values );
356 static void trw_layer_delete_all_routes ( menu_array_layer values );
357 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
360 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
361 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
362 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
363 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
365 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
366 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
368 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
369 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
370 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
371 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
373 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
374 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
375 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
376 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
377 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
378 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
379 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
380 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
381 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
382 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
383 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
384 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
385 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
386 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
387 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
388 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
389 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
390 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
391 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
392 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
393 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
394 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
395 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
396 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
398 static void cached_pixbuf_free ( CachedPixbuf *cp );
399 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
401 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
402 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
404 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
405 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
407 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
408 static void highest_wp_number_reset(VikTrwLayer *vtl);
409 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
410 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
412 // Note for the following tool GtkRadioActionEntry texts:
413 // the very first text value is an internal name not displayed anywhere
414 // the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
415 // * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
416 // the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
417 // the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
418 static VikToolInterface trw_layer_tools[] = {
419 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
420 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
421 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
423 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
425 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
426 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
427 (VikToolMouseFunc) tool_new_track_click,
428 (VikToolMouseMoveFunc) tool_new_track_move,
429 (VikToolMouseFunc) tool_new_track_release,
430 (VikToolKeyFunc) tool_new_track_key_press,
431 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
432 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
434 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
435 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
436 (VikToolMouseFunc) tool_new_route_click,
437 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
438 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
439 (VikToolKeyFunc) tool_new_track_key_press, // -/#
440 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
441 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
443 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
444 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
445 (VikToolMouseFunc) tool_extended_route_finder_click,
446 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
447 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
448 (VikToolKeyFunc) tool_extended_route_finder_key_press,
449 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
450 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
452 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
453 (VikToolConstructorFunc) tool_edit_waypoint_create,
454 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
456 (VikToolMouseFunc) tool_edit_waypoint_click,
457 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
458 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
460 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
462 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
463 (VikToolConstructorFunc) tool_edit_trackpoint_create,
464 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
466 (VikToolMouseFunc) tool_edit_trackpoint_click,
467 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
468 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
470 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
472 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
473 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
474 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
476 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
481 TOOL_CREATE_WAYPOINT=0,
486 TOOL_EDIT_TRACKPOINT,
491 /****** PARAMETERS ******/
493 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
494 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
496 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
497 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
499 #define MIN_POINT_SIZE 2
500 #define MAX_POINT_SIZE 10
502 #define MIN_ARROW_SIZE 3
503 #define MAX_ARROW_SIZE 20
505 static VikLayerParamScale params_scales[] = {
506 /* min max step digits */
507 { 1, 10, 1, 0 }, /* line_thickness */
508 { 0, 100, 1, 0 }, /* track draw speed factor */
509 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
510 /* 5 * step == how much to turn */
511 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
512 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
513 { 5, 500, 5, 0 }, // 5: image cache_size - " "
514 { 0, 8, 1, 0 }, // 6: Background line thickness
515 { 1, 64, 1, 0 }, /* wpsize */
516 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
517 { 1, 100, 1, 0 }, // 9: elevation factor
518 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
519 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
522 static gchar* params_font_sizes[] = {
523 N_("Extra Extra Small"),
529 N_("Extra Extra Large"),
532 // Needs to align with vik_layer_sort_order_t
533 static gchar* params_sort_order[] = {
535 N_("Name Ascending"),
536 N_("Name Descending"),
537 N_("Date Ascending"),
538 N_("Date Descending"),
542 static VikLayerParamData black_color_default ( void ) {
543 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
545 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
546 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
547 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
548 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
549 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
550 static VikLayerParamData trackbgcolor_default ( void ) {
551 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
553 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
554 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
555 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
557 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
558 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
559 static VikLayerParamData wptextcolor_default ( void ) {
560 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
562 static VikLayerParamData wpbgcolor_default ( void ) {
563 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
565 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
566 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
568 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
569 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
570 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
572 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
574 static VikLayerParamData string_default ( void )
576 VikLayerParamData data;
581 VikLayerParam trw_layer_params[] = {
582 { 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 },
583 { 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 },
584 { 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 },
586 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
587 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
588 { 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 },
589 { 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 },
590 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
591 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
592 { 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 },
593 { 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 },
594 { 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 },
595 { 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 },
596 { 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 },
597 { 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 },
598 { 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 },
599 { 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 },
600 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
601 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 },
602 { 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 },
604 { 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 },
605 { 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 },
606 { 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,
607 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
608 { 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 },
610 { 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 },
611 { 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 },
612 { 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 },
613 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
614 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
615 { 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 },
616 { 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 },
617 { 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 },
618 { 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 },
619 { 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 },
621 { 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 },
622 { 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 },
623 { 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 },
624 { 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 },
626 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
627 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
628 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
629 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
632 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
634 // Sublayer visibilities
682 *** 1) Add to trw_layer_params and enumeration
683 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
686 /****** END PARAMETERS ******/
688 /* Layer Interface function definitions */
689 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
690 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
691 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
692 static void trw_layer_free ( VikTrwLayer *trwlayer );
693 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
694 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
695 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl );
696 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
697 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
698 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
699 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
700 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
701 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
702 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
703 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
704 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
705 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
706 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
707 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
708 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
709 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
710 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
711 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
712 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
713 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
714 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
715 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
716 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
717 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
718 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
719 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
720 /* End Layer Interface function definitions */
722 VikLayerInterface vik_trw_layer_interface = {
729 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
733 params_groups, /* params_groups */
734 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
738 (VikLayerFuncCreate) trw_layer_create,
739 (VikLayerFuncRealize) trw_layer_realize,
740 (VikLayerFuncPostRead) trw_layer_post_read,
741 (VikLayerFuncFree) trw_layer_free,
743 (VikLayerFuncProperties) NULL,
744 (VikLayerFuncDraw) trw_layer_draw,
745 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
746 (VikLayerFuncGetTimestamp) trw_layer_get_timestamp,
748 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
749 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
751 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
752 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
754 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
755 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
756 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
757 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
758 (VikLayerFuncLayerSelected) trw_layer_selected,
760 (VikLayerFuncMarshall) trw_layer_marshall,
761 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
763 (VikLayerFuncSetParam) trw_layer_set_param,
764 (VikLayerFuncGetParam) trw_layer_get_param,
765 (VikLayerFuncChangeParam) trw_layer_change_param,
767 (VikLayerFuncReadFileData) a_gpspoint_read_file,
768 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
770 (VikLayerFuncDeleteItem) trw_layer_del_item,
771 (VikLayerFuncCutItem) trw_layer_cut_item,
772 (VikLayerFuncCopyItem) trw_layer_copy_item,
773 (VikLayerFuncPasteItem) trw_layer_paste_item,
774 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
776 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
778 (VikLayerFuncSelectClick) trw_layer_select_click,
779 (VikLayerFuncSelectMove) trw_layer_select_move,
780 (VikLayerFuncSelectRelease) trw_layer_select_release,
781 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
784 static gboolean have_diary_program = FALSE;
785 static gchar *diary_program = NULL;
786 #define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
788 static gboolean have_geojson_export = FALSE;
790 static gboolean have_astro_program = FALSE;
791 static gchar *astro_program = NULL;
792 #define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
794 // NB Only performed once per program run
795 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
797 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
799 //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
800 diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
802 diary_program = g_strdup ( "rednotebook" );
806 // User specified so assume it works
807 have_diary_program = TRUE;
810 if ( g_find_program_in_path( diary_program ) ) {
811 gchar *mystdout = NULL;
812 gchar *mystderr = NULL;
813 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
814 gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version"
815 if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) {
816 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
818 g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
820 g_warning ("Diary: stderr: %s", mystderr );
822 gchar **tokens = NULL;
823 if ( mystdout && g_strcmp0(mystdout, "") )
824 tokens = g_strsplit(mystdout, " ", 0);
826 tokens = g_strsplit(mystderr, " ", 0);
829 gchar *token = tokens[num];
830 while ( token && num < 2 ) {
832 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
833 have_diary_program = TRUE;
838 g_strfreev ( tokens );
845 if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
846 have_geojson_export = TRUE;
850 if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
852 //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
853 astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
855 astro_program = g_strdup ( "stellarium" );
859 // User specified so assume it works
860 have_astro_program = TRUE;
862 if ( g_find_program_in_path( astro_program ) ) {
863 have_astro_program = TRUE;
867 GType vik_trw_layer_get_type ()
869 static GType vtl_type = 0;
873 static const GTypeInfo vtl_info =
875 sizeof (VikTrwLayerClass),
876 NULL, /* base_init */
877 NULL, /* base_finalize */
878 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
879 NULL, /* class_finalize */
880 NULL, /* class_data */
881 sizeof (VikTrwLayer),
883 NULL /* instance init */
885 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
890 VikTRWMetadata *vik_trw_metadata_new()
892 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
895 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
900 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
902 return vtl->metadata;
905 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
908 vik_trw_metadata_free ( vtl->metadata );
909 vtl->metadata = metadata;
914 const gchar *date_str;
916 const VikWaypoint *wpt;
921 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
925 // Might be an easier way to compare dates rather than converting the strings all the time...
926 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
927 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
929 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
938 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
942 // Might be an easier way to compare dates rather than converting the strings all the time...
943 if ( wpt->has_timestamp ) {
944 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
946 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
956 * Find an item by date
958 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
962 df.date_str = date_str;
967 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
969 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
971 if ( select && df.found ) {
972 if ( do_tracks && df.trk ) {
973 struct LatLon maxmin[2] = { {0,0}, {0,0} };
974 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
975 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
976 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
979 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
980 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
982 vik_layer_emit_update ( VIK_LAYER(vtl) );
987 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
989 static menu_array_sublayer values;
995 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
998 values[MA_VTL] = vtl;
999 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
1000 values[MA_SUBLAYER_ID] = sublayer;
1001 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
1003 trw_layer_delete_item ( values );
1006 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
1008 static menu_array_sublayer values;
1014 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
1017 values[MA_VTL] = vtl;
1018 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
1019 values[MA_SUBLAYER_ID] = sublayer;
1020 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
1022 trw_layer_copy_item_cb(values);
1023 trw_layer_cut_item_cb(values);
1026 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
1028 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1029 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
1030 gpointer * sublayer = values[MA_SUBLAYER_ID];
1031 guint8 *data = NULL;
1034 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
1038 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1039 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
1040 if ( wp && wp->name )
1043 name = NULL; // Broken :(
1045 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1046 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1047 if ( trk && trk->name )
1050 name = NULL; // Broken :(
1053 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1054 if ( trk && trk->name )
1057 name = NULL; // Broken :(
1060 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1061 subtype, len, name, data);
1065 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1067 trw_layer_copy_item_cb(values);
1068 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1069 trw_layer_delete_item(values);
1072 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1074 // Slightly cheating method, routing via the panels capability
1075 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1078 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1088 GByteArray *ba = g_byte_array_new ();
1090 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1091 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1092 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1093 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1095 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1098 g_byte_array_append ( ba, id, il );
1106 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1113 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1117 w = vik_waypoint_unmarshall ( item, len );
1118 // When copying - we'll create a new name based on the original
1119 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1120 vik_trw_layer_add_waypoint ( vtl, name, w );
1121 waypoint_convert (NULL, w, &vtl->coord_mode);
1124 trw_layer_calculate_bounds_waypoints ( vtl );
1126 // Consider if redraw necessary for the new item
1127 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1128 vik_layer_emit_update ( VIK_LAYER(vtl) );
1131 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1135 t = vik_track_unmarshall ( item, len );
1136 // When copying - we'll create a new name based on the original
1137 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1138 vik_trw_layer_add_track ( vtl, name, t );
1139 vik_track_convert (t, vtl->coord_mode);
1142 // Consider if redraw necessary for the new item
1143 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1144 vik_layer_emit_update ( VIK_LAYER(vtl) );
1147 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1151 t = vik_track_unmarshall ( item, len );
1152 // When copying - we'll create a new name based on the original
1153 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1154 vik_trw_layer_add_route ( vtl, name, t );
1155 vik_track_convert (t, vtl->coord_mode);
1158 // Consider if redraw necessary for the new item
1159 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1160 vik_layer_emit_update ( VIK_LAYER(vtl) );
1166 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1173 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1177 case PARAM_TV: vtl->tracks_visible = data.b; break;
1178 case PARAM_WV: vtl->waypoints_visible = data.b; break;
1179 case PARAM_RV: vtl->routes_visible = data.b; break;
1180 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1181 case PARAM_TLFONTSIZE:
1182 if ( data.u < FS_NUM_SIZES ) {
1183 vtl->track_font_size = data.u;
1184 g_free ( vtl->track_fsize_str );
1185 switch ( vtl->track_font_size ) {
1186 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1187 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1188 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1189 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1190 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1191 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1192 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1196 case PARAM_DM: vtl->drawmode = data.u; break;
1198 vtl->track_color = data.c;
1199 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1201 case PARAM_DP: vtl->drawpoints = data.b; break;
1203 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1204 vtl->drawpoints_size = data.u;
1206 case PARAM_DE: vtl->drawelevation = data.b; break;
1207 case PARAM_DS: vtl->drawstops = data.b; break;
1208 case PARAM_DL: vtl->drawlines = data.b; break;
1209 case PARAM_DD: vtl->drawdirections = data.b; break;
1211 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1212 vtl->drawdirections_size = data.u;
1214 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1215 vtl->stop_length = data.u;
1217 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1218 vtl->elevation_factor = data.u;
1220 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1222 vtl->line_thickness = data.u;
1223 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1226 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1228 vtl->bg_line_thickness = data.u;
1229 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1233 vtl->track_bg_color = data.c;
1234 if ( vtl->track_bg_gc )
1235 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1237 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1238 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1239 case PARAM_DLA: vtl->drawlabels = data.b; break;
1240 case PARAM_DI: vtl->drawimages = data.b; break;
1241 case PARAM_IS: if ( data.u != vtl->image_size )
1243 vtl->image_size = data.u;
1244 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1245 g_queue_free ( vtl->image_cache );
1246 vtl->image_cache = g_queue_new ();
1249 case PARAM_IA: vtl->image_alpha = data.u; break;
1250 case PARAM_ICS: vtl->image_cache_size = data.u;
1251 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1252 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1255 vtl->waypoint_color = data.c;
1256 if ( vtl->waypoint_gc )
1257 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1260 vtl->waypoint_text_color = data.c;
1261 if ( vtl->waypoint_text_gc )
1262 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1265 vtl->waypoint_bg_color = data.c;
1266 if ( vtl->waypoint_bg_gc )
1267 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1270 vtl->wpbgand = data.b;
1271 if ( vtl->waypoint_bg_gc )
1272 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1274 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1275 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1276 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1277 case PARAM_WPFONTSIZE:
1278 if ( data.u < FS_NUM_SIZES ) {
1279 vtl->wp_font_size = data.u;
1280 g_free ( vtl->wp_fsize_str );
1281 switch ( vtl->wp_font_size ) {
1282 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1283 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1284 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1285 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1286 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1287 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1288 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1292 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1294 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1295 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1296 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1297 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1303 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1305 VikLayerParamData rv;
1308 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1309 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1310 case PARAM_RV: rv.b = vtl->routes_visible; break;
1311 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1312 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1313 case PARAM_DM: rv.u = vtl->drawmode; break;
1314 case PARAM_TC: rv.c = vtl->track_color; break;
1315 case PARAM_DP: rv.b = vtl->drawpoints; break;
1316 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1317 case PARAM_DE: rv.b = vtl->drawelevation; break;
1318 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1319 case PARAM_DS: rv.b = vtl->drawstops; break;
1320 case PARAM_SL: rv.u = vtl->stop_length; break;
1321 case PARAM_DL: rv.b = vtl->drawlines; break;
1322 case PARAM_DD: rv.b = vtl->drawdirections; break;
1323 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1324 case PARAM_LT: rv.u = vtl->line_thickness; break;
1325 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1326 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1327 case PARAM_DI: rv.b = vtl->drawimages; break;
1328 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1329 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1330 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1331 case PARAM_IS: rv.u = vtl->image_size; break;
1332 case PARAM_IA: rv.u = vtl->image_alpha; break;
1333 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1334 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1335 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1336 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1337 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1338 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1339 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1340 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1341 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1342 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1344 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1345 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1346 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1347 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1353 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1355 // This '-3' is to account for the first few parameters not in the properties
1356 const gint OFFSET = -3;
1358 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1359 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1362 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1363 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1364 GtkWidget **ww2 = values[UI_CHG_LABELS];
1365 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1366 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1367 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1368 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1369 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1370 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1371 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1372 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1373 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1374 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1375 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1376 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1379 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1382 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1383 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1384 GtkWidget **ww2 = values[UI_CHG_LABELS];
1385 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1386 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1387 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1388 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1389 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1390 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1391 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1392 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1393 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1394 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1395 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1396 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1397 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1398 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1399 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1400 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1403 // Alter sensitivity of all track colours according to the draw track mode.
1406 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1407 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1408 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1409 GtkWidget **ww2 = values[UI_CHG_LABELS];
1410 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1411 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1412 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1413 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1416 case PARAM_MDTIME: {
1417 // Force metadata->timestamp to be always read-only for now.
1418 GtkWidget **ww = values[UI_CHG_WIDGETS];
1419 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1420 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1422 // NB Since other track settings have been split across tabs,
1423 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1428 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1435 // Use byte arrays to store sublayer data
1436 // much like done elsewhere e.g. vik_layer_marshall_params()
1437 GByteArray *ba = g_byte_array_new ( );
1442 guint object_length;
1445 // the length of the item
1446 // the sublayer type of item
1447 // the the actual item
1448 #define tlm_append(object_pointer, size, type) \
1450 object_length = (size); \
1451 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1452 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1453 g_byte_array_append ( ba, (object_pointer), object_length );
1455 // Layer parameters first
1456 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1457 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1458 g_byte_array_append ( ba, pd, pl );
1461 // Now sublayer data
1462 GHashTableIter iter;
1463 gpointer key, value;
1466 g_hash_table_iter_init ( &iter, vtl->waypoints );
1467 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1468 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1469 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1474 g_hash_table_iter_init ( &iter, vtl->tracks );
1475 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1476 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1477 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1482 g_hash_table_iter_init ( &iter, vtl->routes );
1483 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1484 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1485 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1495 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1497 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1499 gint consumed_length;
1501 // First the overall layer parameters
1502 memcpy(&pl, data, sizeof(pl));
1504 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1507 consumed_length = pl;
1508 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1510 #define tlm_size (*(gint *)data)
1511 // See marshalling above for order of how this is written
1513 data += sizeof_len_and_subtype + tlm_size;
1515 // Now the individual sublayers:
1517 while ( *data && consumed_length < len ) {
1518 // Normally four extra bytes at the end of the datastream
1519 // (since it's a GByteArray and that's where it's length is stored)
1520 // So only attempt read when there's an actual block of sublayer data
1521 if ( consumed_length + tlm_size < len ) {
1523 // Reuse pl to read the subtype from the data stream
1524 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1526 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1527 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1528 gchar *name = g_strdup ( trk->name );
1529 vik_trw_layer_add_track ( vtl, name, trk );
1532 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1533 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1534 gchar *name = g_strdup ( wp->name );
1535 vik_trw_layer_add_waypoint ( vtl, name, wp );
1538 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1539 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1540 gchar *name = g_strdup ( trk->name );
1541 vik_trw_layer_add_route ( vtl, name, trk );
1545 consumed_length += tlm_size + sizeof_len_and_subtype;
1548 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1550 // Not stored anywhere else so need to regenerate
1551 trw_layer_calculate_bounds_waypoints ( vtl );
1556 // Keep interesting hash function at least visible
1558 static guint strcase_hash(gconstpointer v)
1560 // 31 bit hash function
1563 gchar s[128]; // malloc is too slow for reading big files
1566 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1567 p[i] = toupper(t[i]);
1573 for (p += 1; *p != '\0'; p++)
1574 h = (h << 5) - h + *p;
1581 // Stick a 1 at the end of the function name to make it more unique
1582 // thus more easily searchable in a simple text editor
1583 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1585 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1586 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1588 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1589 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1591 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1592 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1593 // and with normal PC processing capabilities - it has negligibile performance impact
1594 // This also minimized the amount of rework - as the management of the hash tables already exists.
1596 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1597 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1598 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1600 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1601 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1602 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1603 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1604 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1605 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1607 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1609 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1611 // Param settings that are not available via the GUI
1612 // Force to on after processing params (which defaults them to off with a zero value)
1613 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1615 rv->metadata = vik_trw_metadata_new ();
1616 rv->draw_sync_done = TRUE;
1617 rv->draw_sync_do = TRUE;
1618 // Everything else is 0, FALSE or NULL
1624 static void trw_layer_free ( VikTrwLayer *trwlayer )
1626 g_hash_table_destroy(trwlayer->waypoints);
1627 g_hash_table_destroy(trwlayer->waypoints_iters);
1628 g_hash_table_destroy(trwlayer->tracks);
1629 g_hash_table_destroy(trwlayer->tracks_iters);
1630 g_hash_table_destroy(trwlayer->routes);
1631 g_hash_table_destroy(trwlayer->routes_iters);
1633 /* ODC: replace with GArray */
1634 trw_layer_free_track_gcs ( trwlayer );
1636 if ( trwlayer->wp_right_click_menu )
1637 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1639 if ( trwlayer->track_right_click_menu )
1640 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1642 if ( trwlayer->tracklabellayout != NULL)
1643 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1645 if ( trwlayer->wplabellayout != NULL)
1646 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1648 if ( trwlayer->waypoint_gc != NULL )
1649 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1651 if ( trwlayer->waypoint_text_gc != NULL )
1652 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1654 if ( trwlayer->waypoint_bg_gc != NULL )
1655 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1657 g_free ( trwlayer->wp_fsize_str );
1658 g_free ( trwlayer->track_fsize_str );
1660 if ( trwlayer->tpwin != NULL )
1661 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1663 if ( trwlayer->tracks_analysis_dialog != NULL )
1664 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1666 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1667 g_queue_free ( trwlayer->image_cache );
1670 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1674 dp->highlight = highlight;
1675 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1676 dp->xmpp = vik_viewport_get_xmpp ( vp );
1677 dp->ympp = vik_viewport_get_ympp ( vp );
1678 dp->width = vik_viewport_get_width ( vp );
1679 dp->height = vik_viewport_get_height ( vp );
1680 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1681 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1683 dp->center = vik_viewport_get_center ( vp );
1684 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1685 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1690 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1691 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1692 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1694 dp->ce1 = dp->center->east_west-w2;
1695 dp->ce2 = dp->center->east_west+w2;
1696 dp->cn1 = dp->center->north_south-h2;
1697 dp->cn2 = dp->center->north_south+h2;
1698 } else if ( dp->lat_lon ) {
1699 VikCoord upperleft, bottomright;
1700 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1701 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1702 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1703 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1704 dp->ce1 = upperleft.east_west;
1705 dp->ce2 = bottomright.east_west;
1706 dp->cn1 = bottomright.north_south;
1707 dp->cn2 = upperleft.north_south;
1710 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1714 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1715 * Here a simple traffic like light colour system is used:
1716 * . slow points are red
1717 * . average is yellow
1718 * . fast points are green
1720 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1723 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1724 if ( average_speed > 0 ) {
1725 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1726 if ( rv < low_speed )
1727 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1728 else if ( rv > high_speed )
1729 return VIK_TRW_LAYER_TRACK_GC_FAST;
1731 return VIK_TRW_LAYER_TRACK_GC_AVER;
1734 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1737 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1739 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1740 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1741 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1742 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1746 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1748 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1750 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1751 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1753 // Fallback if parse failure
1754 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1756 g_free ( label_markup );
1758 gint label_x, label_y;
1760 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1762 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1763 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1767 * distance_in_preferred_units:
1768 * @dist: The source distance in standard SI Units (i.e. metres)
1770 * TODO: This is a generic function that could be moved into globals.c or utils.c
1772 * Probably best used if you have a only few conversions to perform.
1773 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1774 * since it will be doing the preference check on each call
1776 * Returns: The distance in the units as specified by the preferences
1778 static gdouble distance_in_preferred_units ( gdouble dist )
1781 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1782 switch (dist_units) {
1783 case VIK_UNITS_DISTANCE_MILES:
1784 mydist = VIK_METERS_TO_MILES(dist);
1786 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1787 mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1789 // VIK_UNITS_DISTANCE_KILOMETRES:
1791 mydist = dist/1000.0;
1798 * trw_layer_draw_dist_labels:
1800 * Draw a few labels along a track at nicely seperated distances
1801 * This might slow things down if there's many tracks being displayed with this on.
1803 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1805 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1806 25.0, 40.0, 50.0, 75.0, 100.0,
1807 150.0, 200.0, 250.0, 500.0, 1000.0};
1809 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1811 // Convert to specified unit to find the friendly breakdown value
1812 dist = distance_in_preferred_units ( dist );
1816 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1817 if ( chunksd[i] > dist ) {
1819 dist = chunksd[index];
1824 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1826 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1827 gdouble dist_i = dist * i;
1829 // Convert distance back into metres for use in finding a trackpoint
1830 switch (dist_units) {
1831 case VIK_UNITS_DISTANCE_MILES:
1832 dist_i = VIK_MILES_TO_METERS(dist_i);
1834 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1835 dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1837 // VIK_UNITS_DISTANCE_KILOMETRES:
1839 dist_i = dist_i*1000.0;
1843 gdouble dist_current = 0.0;
1844 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1845 gdouble dist_next = 0.0;
1846 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1848 gdouble dist_between_tps = fabs (dist_next - dist_current);
1849 gdouble ratio = 0.0;
1850 // Prevent division by 0 errors
1851 if ( dist_between_tps > 0.0 )
1852 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1854 if ( tp_current && tp_next ) {
1855 // Construct the name based on the distance value
1858 switch (dist_units) {
1859 case VIK_UNITS_DISTANCE_MILES:
1860 units = g_strdup ( _("miles") );
1862 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1863 units = g_strdup ( _("NM") );
1865 // VIK_UNITS_DISTANCE_KILOMETRES:
1867 units = g_strdup ( _("km") );
1871 // Convert for display
1872 dist_i = distance_in_preferred_units ( dist_i );
1874 // Make the precision of the output related to the unit size.
1876 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1877 else if ( index == 1 )
1878 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1880 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1883 struct LatLon ll_current, ll_next;
1884 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1885 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1887 // positional interpolation
1888 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1889 // but should be good enough over the small scale that I anticipate usage on
1890 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1891 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1893 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1896 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1897 fgcolour = gdk_color_to_string ( &(trk->color) );
1899 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1901 // if highlight mode on, then colour the background in the highlight colour
1903 if ( drawing_highlight )
1904 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1906 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1908 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1910 g_free ( fgcolour );
1911 g_free ( bgcolour );
1918 * trw_layer_draw_track_name_labels:
1920 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1922 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1925 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1926 fgcolour = gdk_color_to_string ( &(trk->color) );
1928 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1930 // if highlight mode on, then colour the background in the highlight colour
1932 if ( drawing_highlight )
1933 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1935 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1937 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1939 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1940 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1941 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1942 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1943 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1944 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1946 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1948 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1951 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1952 // No other labels to draw
1955 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1958 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1961 VikCoord begin_coord = tp_begin->coord;
1962 VikCoord end_coord = tp_end->coord;
1964 gboolean done_start_end = FALSE;
1966 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1967 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1969 // This number can be configured via the settings if you really want to change it
1970 gdouble distance_diff;
1971 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1972 distance_diff = 100.0; // Metres
1974 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1975 // Start and end 'close' together so only draw one label at an average location
1976 gint x1, x2, y1, y2;
1977 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1978 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1980 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1982 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1983 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1986 done_start_end = TRUE;
1990 if ( ! done_start_end ) {
1991 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1992 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1993 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1994 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1995 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1996 g_free ( name_start );
1998 // Don't draw end label if this is the one being created
1999 if ( trk != dp->vtl->current_track ) {
2000 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
2001 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
2002 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
2003 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
2004 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
2005 g_free ( name_end );
2010 g_free ( fgcolour );
2011 g_free ( bgcolour );
2015 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
2017 if ( ! track->visible )
2020 /* TODO: this function is a mess, get rid of any redundancy */
2021 GList *list = track->trackpoints;
2023 gboolean useoldvals = TRUE;
2025 gboolean drawpoints;
2027 gboolean drawelevation;
2028 gdouble min_alt, max_alt, alt_diff = 0;
2030 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
2031 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
2034 if ( dp->vtl->drawelevation )
2036 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
2037 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
2038 alt_diff = max_alt - min_alt;
2041 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
2042 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
2043 trw_layer_draw_track ( id, track, dp, TRUE );
2045 if ( draw_track_outline )
2046 drawpoints = drawstops = FALSE;
2048 drawpoints = dp->vtl->drawpoints;
2049 drawstops = dp->vtl->drawstops;
2052 gboolean drawing_highlight = FALSE;
2053 /* Current track - used for creation */
2054 if ( track == dp->vtl->current_track )
2055 main_gc = dp->vtl->current_track_gc;
2057 if ( dp->highlight ) {
2058 /* Draw all tracks of the layer in special colour
2059 NB this supercedes the drawmode */
2060 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2061 drawing_highlight = TRUE;
2063 if ( !drawing_highlight ) {
2064 // Still need to figure out the gc according to the drawing mode:
2065 switch ( dp->vtl->drawmode ) {
2066 case DRAWMODE_BY_TRACK:
2067 if ( dp->vtl->track_1color_gc )
2068 g_object_unref ( dp->vtl->track_1color_gc );
2069 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2070 main_gc = dp->vtl->track_1color_gc;
2073 // Mostly for DRAWMODE_ALL_SAME_COLOR
2074 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2075 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2082 int x, y, oldx, oldy;
2083 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2085 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2087 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2089 // Draw the first point as something a bit different from the normal points
2090 // ATM it's slightly bigger and a triangle
2092 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2093 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2099 gdouble average_speed = 0.0;
2100 gdouble low_speed = 0.0;
2101 gdouble high_speed = 0.0;
2102 // If necessary calculate these values - which is done only once per track redraw
2103 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2104 // the percentage factor away from the average speed determines transistions between the levels
2105 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2106 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2107 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2110 while ((list = g_list_next(list)))
2112 tp = VIK_TRACKPOINT(list->data);
2113 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2115 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2116 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2117 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2118 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2119 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2121 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2124 * If points are the same in display coordinates, don't draw.
2126 if ( useoldvals && x == oldx && y == oldy )
2128 // Still need to process points to ensure 'stops' are drawn if required
2129 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2130 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2131 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 );
2136 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2137 if ( drawpoints || dp->vtl->drawlines ) {
2138 // setup main_gc for both point and line drawing
2139 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2140 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 ) );
2144 if ( drawpoints && ! draw_track_outline )
2149 * The concept of drawing stops is that a trackpoint
2150 * that is if the next trackpoint has a timestamp far into
2151 * the future, we draw a circle of 6x trackpoint size,
2152 * instead of a rectangle of 2x trackpoint size.
2153 * This is drawn first so the trackpoint will be drawn on top
2156 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2157 /* Stop point. Draw 6x circle. Always in redish colour */
2158 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 );
2160 /* Regular point - draw 2x square. */
2161 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2164 /* Final point - draw 4x circle. */
2165 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 );
2168 if ((!tp->newsegment) && (dp->vtl->drawlines))
2171 /* UTM only: zone check */
2172 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2173 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
2176 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2178 if ( draw_track_outline ) {
2179 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2183 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2185 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2187 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2192 tmp[1].y = oldy-FIXALTITUDE(list->data);
2194 tmp[2].y = y-FIXALTITUDE(list->next->data);
2199 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2200 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2202 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2203 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2205 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2210 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2211 // Draw an arrow at the mid point to show the direction of the track
2212 // Code is a rework from vikwindow::draw_ruler()
2213 gint midx = (oldx + x) / 2;
2214 gint midy = (oldy + y) / 2;
2216 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2217 // Avoid divide by zero and ensure at least 1 pixel big
2219 gdouble dx = (oldx - midx) / len;
2220 gdouble dy = (oldy - midy) / len;
2221 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2222 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2232 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2234 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2235 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2237 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2239 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2240 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 ));
2244 * If points are the same in display coordinates, don't draw.
2246 if ( x != oldx || y != oldy )
2248 if ( draw_track_outline )
2249 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2251 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2257 * If points are the same in display coordinates, don't draw.
2259 if ( x != oldx && y != oldy )
2261 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2262 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2270 // Labels drawn after the trackpoints, so the labels are on top
2271 if ( dp->vtl->track_draw_labels ) {
2272 if ( track->max_number_dist_labels > 0 ) {
2273 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2276 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2277 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2283 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2285 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2286 trw_layer_draw_track ( id, track, dp, FALSE );
2290 static void cached_pixbuf_free ( CachedPixbuf *cp )
2292 g_object_unref ( G_OBJECT(cp->pixbuf) );
2293 g_free ( cp->image );
2296 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2298 return strcmp ( cp->image, name );
2301 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2304 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
2305 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2306 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2309 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2311 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2313 if ( wp->image && dp->vtl->drawimages )
2315 GdkPixbuf *pixbuf = NULL;
2318 if ( dp->vtl->image_alpha == 0)
2321 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2323 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2326 gchar *image = wp->image;
2327 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2328 if ( ! regularthumb )
2330 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2331 image = "\x12\x00"; /* this shouldn't occur naturally. */
2335 CachedPixbuf *cp = NULL;
2336 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2337 if ( dp->vtl->image_size == 128 )
2338 cp->pixbuf = regularthumb;
2341 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2342 g_assert ( cp->pixbuf );
2343 g_object_unref ( G_OBJECT(regularthumb) );
2345 cp->image = g_strdup ( image );
2347 /* needed so 'click picture' tool knows how big the pic is; we don't
2348 * store it in cp because they may have been freed already. */
2349 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2350 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2352 g_queue_push_head ( dp->vtl->image_cache, cp );
2353 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2354 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2356 pixbuf = cp->pixbuf;
2360 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2366 w = gdk_pixbuf_get_width ( pixbuf );
2367 h = gdk_pixbuf_get_height ( pixbuf );
2369 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2371 if ( dp->highlight ) {
2372 // Highlighted - so draw a little border around the chosen one
2373 // single line seems a little weak so draw 2 of them
2374 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2375 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2376 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2377 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2380 if ( dp->vtl->image_alpha == 255 )
2381 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2383 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2385 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2389 // Draw appropriate symbol - either symbol image or simple types
2390 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2391 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 );
2393 else if ( wp == dp->vtl->current_wp ) {
2394 switch ( dp->vtl->wp_symbol ) {
2395 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;
2396 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;
2397 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;
2398 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 );
2399 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 );
2404 switch ( dp->vtl->wp_symbol ) {
2405 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;
2406 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;
2407 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;
2408 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 );
2409 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;
2414 if ( dp->vtl->drawlabels )
2416 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2417 gint label_x, label_y;
2419 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2421 // Could this stored in the waypoint rather than recreating each pass?
2422 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2424 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2425 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2427 // Fallback if parse failure
2428 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2430 g_free ( wp_label_markup );
2432 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2433 label_x = x - width/2;
2434 if ( wp->symbol_pixbuf )
2435 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2437 label_y = y - dp->vtl->wp_size - height - 2;
2439 /* if highlight mode on, then draw background text in highlight colour */
2440 if ( dp->highlight )
2441 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2443 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2444 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2449 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2451 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2452 trw_layer_draw_waypoint ( id, wp, dp );
2456 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2458 static struct DrawingParams dp;
2459 g_assert ( l != NULL );
2461 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2463 if ( l->tracks_visible )
2464 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2466 if ( l->routes_visible )
2467 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2469 if (l->waypoints_visible)
2470 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2473 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2475 // 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
2476 // This may seem slightly inefficient to test each time for every layer
2477 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2478 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2479 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2481 trw_layer_draw_with_highlight ( l, data, FALSE );
2484 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2486 // Check the layer for visibility (including all the parents visibilities)
2487 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2489 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2493 * vik_trw_layer_draw_highlight_item:
2495 * Only handles a single track or waypoint ATM
2496 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2498 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2500 // Check the layer for visibility (including all the parents visibilities)
2501 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2504 static struct DrawingParams dp;
2505 init_drawing_params ( &dp, vtl, vvp, TRUE );
2508 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2510 trw_layer_draw_track_cb ( NULL, trk, &dp );
2512 if ( vtl->waypoints_visible && wpt ) {
2513 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2518 * vik_trw_layer_draw_highlight_item:
2520 * Generally for drawing all tracks or routes or waypoints
2521 * trks may be actually routes
2522 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2524 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2526 // Check the layer for visibility (including all the parents visibilities)
2527 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2530 static struct DrawingParams dp;
2531 init_drawing_params ( &dp, vtl, vvp, TRUE );
2534 gboolean is_routes = (trks == vtl->routes);
2535 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2537 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2540 if ( vtl->waypoints_visible && wpts )
2541 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2544 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2547 if ( vtl->track_bg_gc )
2549 g_object_unref ( vtl->track_bg_gc );
2550 vtl->track_bg_gc = NULL;
2552 if ( vtl->track_1color_gc )
2554 g_object_unref ( vtl->track_1color_gc );
2555 vtl->track_1color_gc = NULL;
2557 if ( vtl->current_track_gc )
2559 g_object_unref ( vtl->current_track_gc );
2560 vtl->current_track_gc = NULL;
2562 if ( vtl->current_track_newpoint_gc )
2564 g_object_unref ( vtl->current_track_newpoint_gc );
2565 vtl->current_track_newpoint_gc = NULL;
2568 if ( ! vtl->track_gc )
2570 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2571 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2572 g_array_free ( vtl->track_gc, TRUE );
2573 vtl->track_gc = NULL;
2576 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2578 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2579 gint width = vtl->line_thickness;
2581 if ( vtl->track_gc )
2582 trw_layer_free_track_gcs ( vtl );
2584 if ( vtl->track_bg_gc )
2585 g_object_unref ( vtl->track_bg_gc );
2586 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2588 // Ensure new track drawing heeds line thickness setting
2589 // however always have a minium of 2, as 1 pixel is really narrow
2590 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2592 if ( vtl->current_track_gc )
2593 g_object_unref ( vtl->current_track_gc );
2594 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2595 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2597 // 'newpoint' gc is exactly the same as the current track gc
2598 if ( vtl->current_track_newpoint_gc )
2599 g_object_unref ( vtl->current_track_newpoint_gc );
2600 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2601 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2603 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2605 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2606 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2608 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2609 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2610 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2612 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2614 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2617 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2619 VikTrwLayer *rv = trw_layer_new1 ( vp );
2620 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2622 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2623 /* early exit, as the rest is GUI related */
2627 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2628 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2630 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2631 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2633 trw_layer_new_track_gcs ( rv, vp );
2635 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2636 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2637 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2638 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2640 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2642 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2647 #define SMALL_ICON_SIZE 18
2649 * Can accept a null symbol, and may return null value
2651 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2653 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2654 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2655 // So needing a small icon for the treeview may need some resizing:
2656 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2657 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2661 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2663 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2665 GdkPixbuf *pixbuf = NULL;
2667 if ( track->has_color ) {
2668 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2669 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2670 // Here is some magic found to do the conversion
2671 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2672 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2673 ((track->color.green & 0xff00) << 8) |
2674 (track->color.blue & 0xff00);
2676 gdk_pixbuf_fill ( pixbuf, pixel );
2679 time_t timestamp = 0;
2680 VikTrackpoint *tpt = vik_track_get_tp_first(track);
2681 if ( tpt && tpt->has_timestamp )
2682 timestamp = tpt->timestamp;
2684 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 );
2687 g_object_unref (pixbuf);
2689 *new_iter = *((GtkTreeIter *) pass_along[1]);
2690 if ( track->is_route )
2691 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2693 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2695 if ( ! track->visible )
2696 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2699 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2701 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2703 time_t timestamp = 0;
2704 if ( wp->has_timestamp )
2705 timestamp = wp->timestamp;
2707 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 );
2709 *new_iter = *((GtkTreeIter *) pass_along[1]);
2710 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2712 if ( ! wp->visible )
2713 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2716 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2718 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 );
2721 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2723 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 );
2726 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2728 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 );
2731 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2734 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2736 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2737 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2739 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2741 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2744 if ( g_hash_table_size (vtl->routes) > 0 ) {
2745 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2747 pass_along[0] = &(vtl->routes_iter);
2748 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2750 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2752 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2755 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2756 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2758 pass_along[0] = &(vtl->waypoints_iter);
2759 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2761 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2763 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2768 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2772 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2773 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2774 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2775 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2777 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2779 return (t->visible ^= 1);
2783 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2785 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2787 return (t->visible ^= 1);
2791 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2793 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2795 return (t->visible ^= 1);
2805 * Return a property about tracks for this layer
2807 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2809 return vtl->line_thickness;
2813 * Build up multiple routes information
2815 static void trw_layer_routes_tooltip ( const gpointer id, VikTrack *tr, gdouble *length )
2817 *length = *length + vik_track_get_length (tr);
2820 // Structure to hold multiple track information for a layer
2829 * Build up layer multiple track information via updating the tooltip_tracks structure
2831 static void trw_layer_tracks_tooltip ( const gpointer id, VikTrack *tr, tooltip_tracks *tt )
2833 tt->length = tt->length + vik_track_get_length (tr);
2835 // Ensure times are available
2836 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2837 // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2838 VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2839 if ( trkpt_last->has_timestamp ) {
2841 t1 = vik_track_get_tp_first(tr)->timestamp;
2842 t2 = trkpt_last->timestamp;
2844 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2845 // Hence initialize to the first 'proper' value
2846 if ( tt->start_time == 0 )
2847 tt->start_time = t1;
2848 if ( tt->end_time == 0 )
2851 // Update find the earliest / last times
2852 if ( t1 < tt->start_time )
2853 tt->start_time = t1;
2854 if ( t2 > tt->end_time )
2857 // Keep track of total time
2858 // there maybe gaps within a track (eg segments)
2859 // but this should be generally good enough for a simple indicator
2860 tt->duration = tt->duration + (int)(t2-t1);
2866 * Generate tooltip text for the layer.
2867 * This is relatively complicated as it considers information for
2868 * no tracks, a single track or multiple tracks
2869 * (which may or may not have timing information)
2871 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2882 static gchar tmp_buf[128];
2885 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2887 // Safety check - I think these should always be valid
2888 if ( vtl->tracks && vtl->waypoints ) {
2889 tooltip_tracks tt = { 0.0, 0, 0, 0 };
2890 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2892 GDate* gdate_start = g_date_new ();
2893 g_date_set_time_t (gdate_start, tt.start_time);
2895 GDate* gdate_end = g_date_new ();
2896 g_date_set_time_t (gdate_end, tt.end_time);
2898 if ( g_date_compare (gdate_start, gdate_end) ) {
2899 // Dates differ so print range on separate line
2900 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2901 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2902 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2905 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2906 if ( tt.start_time != 0 )
2907 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2911 if ( tt.length > 0.0 ) {
2912 gdouble len_in_units;
2914 // Setup info dependent on distance units
2915 switch ( a_vik_get_units_distance() ) {
2916 case VIK_UNITS_DISTANCE_MILES:
2917 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2918 len_in_units = VIK_METERS_TO_MILES(tt.length);
2920 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2921 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2922 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2925 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2926 len_in_units = tt.length/1000.0;
2930 // Timing information if available
2932 if ( tt.duration > 0 ) {
2933 g_snprintf (tbuf1, sizeof(tbuf1),
2934 _(" in %d:%02d hrs:mins"),
2935 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2937 g_snprintf (tbuf2, sizeof(tbuf2),
2938 _("\n%sTotal Length %.1f %s%s"),
2939 tbuf3, len_in_units, tbuf4, tbuf1);
2943 gdouble rlength = 0.0;
2944 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_routes_tooltip, &rlength );
2945 if ( rlength > 0.0 ) {
2946 gdouble len_in_units;
2947 // Setup info dependent on distance units
2948 switch ( a_vik_get_units_distance() ) {
2949 case VIK_UNITS_DISTANCE_MILES:
2950 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2951 len_in_units = VIK_METERS_TO_MILES(rlength);
2953 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2954 g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2955 len_in_units = VIK_METERS_TO_NAUTICAL_MILES(rlength);
2958 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2959 len_in_units = rlength/1000.0;
2962 g_snprintf (tbuf1, sizeof(tbuf1), _("\nTotal route length %.1f %s"), len_in_units, tbuf4);
2965 // Put together all the elements to form compact tooltip text
2966 g_snprintf (tmp_buf, sizeof(tmp_buf),
2967 _("Tracks: %d - Waypoints: %d - Routes: %d%s%s"),
2968 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2, tbuf1);
2970 g_date_free (gdate_start);
2971 g_date_free (gdate_end);
2977 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2981 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2983 // Very simple tooltip - may expand detail in the future...
2984 static gchar tmp_buf[32];
2985 g_snprintf (tmp_buf, sizeof(tmp_buf),
2987 g_hash_table_size (l->tracks));
2991 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2993 // Very simple tooltip - may expand detail in the future...
2994 static gchar tmp_buf[32];
2995 g_snprintf (tmp_buf, sizeof(tmp_buf),
2997 g_hash_table_size (l->routes));
3002 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3003 // Same tooltip for a route
3004 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3007 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3008 tr = g_hash_table_lookup ( l->tracks, sublayer );
3010 tr = g_hash_table_lookup ( l->routes, sublayer );
3013 // Could be a better way of handling strings - but this works...
3014 gchar time_buf1[20];
3015 gchar time_buf2[20];
3016 time_buf1[0] = '\0';
3017 time_buf2[0] = '\0';
3018 static gchar tmp_buf[100];
3019 // Compact info: Short date eg (11/20/99), duration and length
3020 // Hopefully these are the things that are most useful and so promoted into the tooltip
3021 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
3022 // %x The preferred date representation for the current locale without the time.
3023 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
3024 time_t dur = vik_track_get_duration ( tr );
3026 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
3028 // Get length and consider the appropriate distance units
3029 gdouble tr_len = vik_track_get_length(tr);
3030 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3031 switch (dist_units) {
3032 case VIK_UNITS_DISTANCE_KILOMETRES:
3033 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
3035 case VIK_UNITS_DISTANCE_MILES:
3036 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
3038 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3039 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
3048 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3050 // Very simple tooltip - may expand detail in the future...
3051 static gchar tmp_buf[32];
3052 g_snprintf (tmp_buf, sizeof(tmp_buf),
3054 g_hash_table_size (l->waypoints));
3058 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3060 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
3061 // NB It's OK to return NULL
3066 return w->description;
3075 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
3078 * set_statusbar_msg_info_trkpt:
3080 * Function to show track point information on the statusbar
3081 * Items displayed is controlled by the settings format code
3083 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
3085 gchar *statusbar_format_code = NULL;
3086 gboolean need2free = FALSE;
3087 VikTrackpoint *trkpt_prev = NULL;
3088 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3089 // Otherwise use default
3090 statusbar_format_code = g_strdup ( "KEATDN" );
3094 // Format code may want to show speed - so may need previous trkpt to work it out
3095 trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
3098 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
3099 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3103 g_free ( statusbar_format_code );
3107 * Function to show basic waypoint information on the statusbar
3109 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3112 switch (a_vik_get_units_height ()) {
3113 case VIK_UNITS_HEIGHT_FEET:
3114 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3117 //VIK_UNITS_HEIGHT_METRES:
3118 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3122 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3123 // one can easily use the current pointer position to see this if needed
3124 gchar *lat = NULL, *lon = NULL;
3125 static struct LatLon ll;
3126 vik_coord_to_latlon (&(wpt->coord), &ll);
3127 a_coords_latlon_to_string ( &ll, &lat, &lon );
3129 // Combine parts to make overall message
3132 // Add comment if available
3133 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3135 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3136 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3143 * General layer selection function, find out which bit is selected and take appropriate action
3145 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3148 l->current_wp = NULL;
3149 l->current_wp_id = NULL;
3150 trw_layer_cancel_current_tp ( l, FALSE );
3153 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3157 case VIK_TREEVIEW_TYPE_LAYER:
3159 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3160 /* Mark for redraw */
3165 case VIK_TREEVIEW_TYPE_SUBLAYER:
3169 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3171 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3172 /* Mark for redraw */
3176 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3178 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3179 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3180 /* Mark for redraw */
3184 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3186 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3187 /* Mark for redraw */
3191 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3193 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3194 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3195 /* Mark for redraw */
3199 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3201 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3202 /* Mark for redraw */
3206 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3208 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3210 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3211 // Show some waypoint info
3212 set_statusbar_msg_info_wpt ( l, wpt );
3213 /* Mark for redraw */
3220 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3229 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3234 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3239 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3244 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3246 return l->waypoints;
3249 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3251 return vtl->tracks_iters;
3254 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3256 return vtl->routes_iters;
3259 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3261 return vtl->waypoints;
3264 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3266 return ! ( g_hash_table_size ( vtl->tracks ) ||
3267 g_hash_table_size ( vtl->routes ) ||
3268 g_hash_table_size ( vtl->waypoints ) );
3271 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3273 return vtl->tracks_visible;
3276 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3278 return vtl->routes_visible;
3281 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3283 return vtl->waypoints_visible;
3287 * ATM use a case sensitive find
3288 * Finds the first one
3290 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3292 if ( wp && wp->name )
3293 if ( ! strcmp ( wp->name, name ) )
3299 * Get waypoint by name - not guaranteed to be unique
3300 * Finds the first one
3302 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3304 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3308 * ATM use a case sensitive find
3309 * Finds the first one
3311 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3313 if ( trk && trk->name )
3314 if ( ! strcmp ( trk->name, name ) )
3320 * Get track by name - not guaranteed to be unique
3321 * Finds the first one
3323 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3325 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3329 * Get route by name - not guaranteed to be unique
3330 * Finds the first one
3332 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3334 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3337 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3339 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3340 maxmin[0].lat = trk->bbox.north;
3341 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3342 maxmin[1].lat = trk->bbox.south;
3343 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3344 maxmin[0].lon = trk->bbox.east;
3345 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3346 maxmin[1].lon = trk->bbox.west;
3349 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3351 // Continually reuse maxmin to find the latest maximum and minimum values
3352 // First set to waypoints bounds
3353 maxmin[0].lat = vtl->waypoints_bbox.north;
3354 maxmin[1].lat = vtl->waypoints_bbox.south;
3355 maxmin[0].lon = vtl->waypoints_bbox.east;
3356 maxmin[1].lon = vtl->waypoints_bbox.west;
3357 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3358 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3361 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3363 /* 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... */
3364 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3365 trw_layer_find_maxmin (vtl, maxmin);
3366 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3370 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3371 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3376 static void trw_layer_centerize ( menu_array_layer values )
3378 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3380 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3381 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3383 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3386 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3388 /* First set the center [in case previously viewing from elsewhere] */
3389 /* Then loop through zoom levels until provided positions are in view */
3390 /* This method is not particularly fast - but should work well enough */
3391 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3393 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3394 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3396 /* Convert into definite 'smallest' and 'largest' positions */
3397 struct LatLon minmin;
3398 if ( maxmin[0].lat < maxmin[1].lat )
3399 minmin.lat = maxmin[0].lat;
3401 minmin.lat = maxmin[1].lat;
3403 struct LatLon maxmax;
3404 if ( maxmin[0].lon > maxmin[1].lon )
3405 maxmax.lon = maxmin[0].lon;
3407 maxmax.lon = maxmin[1].lon;
3409 /* Never zoom in too far - generally not that useful, as too close ! */
3410 /* Always recalculate the 'best' zoom level */
3412 vik_viewport_set_zoom ( vvp, zoom );
3414 gdouble min_lat, max_lat, min_lon, max_lon;
3415 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3416 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3417 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3418 /* NB I think the logic used in this test to determine if the bounds is within view
3419 fails if track goes across 180 degrees longitude.
3420 Hopefully that situation is not too common...
3421 Mind you viking doesn't really do edge locations to well anyway */
3422 if ( min_lat < minmin.lat &&
3423 max_lat > minmin.lat &&
3424 min_lon < maxmax.lon &&
3425 max_lon > maxmax.lon )
3426 /* Found within zoom level */
3431 vik_viewport_set_zoom ( vvp, zoom );
3435 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3437 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3438 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3439 trw_layer_find_maxmin (vtl, maxmin);
3440 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3443 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3448 static void trw_layer_auto_view ( menu_array_layer values )
3450 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3451 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3452 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3453 vik_layers_panel_emit_update ( vlp );
3456 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3459 static void trw_layer_export_gpspoint ( menu_array_layer values )
3461 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3463 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3465 g_free ( auto_save_name );
3468 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3470 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3472 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3474 g_free ( auto_save_name );
3477 static void trw_layer_export_gpx ( menu_array_layer values )
3479 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3481 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3483 g_free ( auto_save_name );
3486 static void trw_layer_export_kml ( menu_array_layer values )
3488 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3490 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3492 g_free ( auto_save_name );
3495 static void trw_layer_export_geojson ( menu_array_layer values )
3497 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3499 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3501 g_free ( auto_save_name );
3504 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3506 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3507 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3510 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3512 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3515 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3517 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3520 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3522 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3524 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3525 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3527 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3529 if ( !trk || !trk->name )
3532 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3534 gchar *label = NULL;
3535 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3536 label = _("Export Route as GPX");
3538 label = _("Export Track as GPX");
3539 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3541 g_free ( auto_save_name );
3544 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3546 wpu_udata *user_data = udata;
3547 if ( wp == user_data->wp ) {
3548 user_data->uuid = id;
3554 static void trw_layer_goto_wp ( menu_array_layer values )
3556 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3557 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3558 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3559 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3560 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3562 GTK_RESPONSE_REJECT,
3564 GTK_RESPONSE_ACCEPT,
3567 GtkWidget *label, *entry;
3568 label = gtk_label_new(_("Waypoint Name:"));
3569 entry = gtk_entry_new();
3571 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3572 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3573 gtk_widget_show_all ( label );
3574 gtk_widget_show_all ( entry );
3576 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3578 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3580 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3581 // Find *first* wp with the given name
3582 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3585 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3588 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3589 vik_layers_panel_emit_update ( vlp );
3591 // Find and select on the side panel
3596 // Hmmm, want key of it
3597 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3599 if ( wpf && udata.uuid ) {
3600 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3601 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3610 gtk_widget_destroy ( dia );
3613 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3615 gchar *default_name = highest_wp_number_get(vtl);
3616 VikWaypoint *wp = vik_waypoint_new();
3617 gchar *returned_name;
3619 wp->coord = *def_coord;
3621 // Attempt to auto set height if DEM data is available
3622 vik_waypoint_apply_dem_data ( wp, TRUE );
3624 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3626 if ( returned_name )
3629 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3630 g_free (default_name);
3631 g_free (returned_name);
3634 g_free (default_name);
3635 vik_waypoint_free(wp);
3639 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3641 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3642 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3643 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3644 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3645 VikViewport *vvp = vik_window_viewport(vw);
3647 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3648 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3649 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3650 trw_layer_calculate_bounds_waypoints ( vtl );
3651 vik_layers_panel_emit_update ( vlp );
3654 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3656 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3657 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3658 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3660 trw_layer_find_maxmin (vtl, maxmin);
3661 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3662 trw_layer_calculate_bounds_waypoints ( vtl );
3663 vik_layers_panel_emit_update ( vlp );
3666 #ifdef VIK_CONFIG_GEOTAG
3667 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3669 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3671 // Update directly - not changing the mtime
3672 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3675 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3677 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3680 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3684 * Use code in separate file for this feature as reasonably complex
3686 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3688 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3689 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3690 // Unset so can be reverified later if necessary
3691 vtl->has_verified_thumbnails = FALSE;
3693 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3699 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3701 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3702 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3704 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3710 static void trw_layer_geotagging ( menu_array_layer values )
3712 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3713 // Unset so can be reverified later if necessary
3714 vtl->has_verified_thumbnails = FALSE;
3716 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3723 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3725 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3727 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3728 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3729 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3730 VikViewport *vvp = vik_window_viewport(vw);
3732 vik_datasource_mode_t mode = datasource->mode;
3733 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3734 mode = VIK_DATASOURCE_ADDTOLAYER;
3735 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3739 * Acquire into this TRW Layer straight from GPS Device
3741 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3743 trw_layer_acquire ( values, &vik_datasource_gps_interface );
3747 * Acquire into this TRW Layer from Directions
3749 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3751 trw_layer_acquire ( values, &vik_datasource_routing_interface );
3755 * Acquire into this TRW Layer from an entered URL
3757 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3759 trw_layer_acquire ( values, &vik_datasource_url_interface );
3762 #ifdef VIK_CONFIG_OPENSTREETMAP
3764 * Acquire into this TRW Layer from OSM
3766 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3768 trw_layer_acquire ( values, &vik_datasource_osm_interface );
3772 * Acquire into this TRW Layer from OSM for 'My' Traces
3774 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3776 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3780 #ifdef VIK_CONFIG_GEOCACHES
3782 * Acquire into this TRW Layer from Geocaching.com
3784 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3786 trw_layer_acquire ( values, &vik_datasource_gc_interface );
3790 #ifdef VIK_CONFIG_GEOTAG
3792 * Acquire into this TRW Layer from images
3794 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3796 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3798 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3800 // Reverify thumbnails as they may have changed
3801 vtl->has_verified_thumbnails = FALSE;
3802 trw_layer_verify_thumbnails ( vtl, NULL );
3807 * Acquire into this TRW Layer from any GPS Babel supported file
3809 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3811 trw_layer_acquire ( values, &vik_datasource_file_interface );
3814 static void trw_layer_gps_upload ( menu_array_layer values )
3816 menu_array_sublayer data;
3818 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3820 data[MA_VTL] = values[MA_VTL];
3821 data[MA_VLP] = values[MA_VLP];
3823 trw_layer_gps_upload_any ( data );
3827 * If pass_along[3] is defined that this will upload just that track
3829 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3831 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3832 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3834 // May not actually get a track here as values[2&3] can be null
3835 VikTrack *track = NULL;
3836 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3837 gboolean xfer_all = FALSE;
3839 if ( values[MA_SUBTYPE] ) {
3841 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3842 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3845 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3846 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3849 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3852 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3856 else if ( !values[MA_CONFIRM] )
3857 xfer_all = TRUE; // i.e. whole layer
3859 if (track && !track->visible) {
3860 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3864 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3865 VIK_GTK_WINDOW_FROM_LAYER(vtl),
3866 GTK_DIALOG_DESTROY_WITH_PARENT,
3868 GTK_RESPONSE_ACCEPT,
3870 GTK_RESPONSE_REJECT,
3873 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3874 GtkWidget *response_w = NULL;
3875 #if GTK_CHECK_VERSION (2, 20, 0)
3876 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3880 gtk_widget_grab_focus ( response_w );
3882 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3884 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3885 datasource_gps_clean_up ( dgs );
3886 gtk_widget_destroy ( dialog );
3890 // Get info from reused datasource dialog widgets
3891 gchar* protocol = datasource_gps_get_protocol ( dgs );
3892 gchar* port = datasource_gps_get_descriptor ( dgs );
3893 // NB don't free the above strings as they're references to values held elsewhere
3894 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3895 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3896 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3897 gboolean turn_off = datasource_gps_get_off ( dgs );
3899 gtk_widget_destroy ( dialog );
3901 // When called from the viewport - work the corresponding layerspanel:
3903 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3906 // Apply settings to transfer to the GPS device
3913 vik_layers_panel_get_viewport (vlp),
3921 static void trw_layer_new_wp ( menu_array_layer values )
3923 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3924 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3925 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3926 instead return true if you want to update. */
3927 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 ) {
3928 trw_layer_calculate_bounds_waypoints ( vtl );
3929 vik_layers_panel_emit_update ( vlp );
3933 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3935 vtl->current_track = vik_track_new();
3936 vik_track_set_defaults ( vtl->current_track );
3937 vtl->current_track->visible = TRUE;
3938 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3939 // Create track with the preferred colour from the layer properties
3940 vtl->current_track->color = vtl->track_color;
3942 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3943 vtl->current_track->has_color = TRUE;
3944 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3947 static void trw_layer_new_track ( menu_array_layer values )
3949 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3951 if ( ! vtl->current_track ) {
3952 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3953 new_track_create_common ( vtl, name );
3956 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3960 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3962 vtl->current_track = vik_track_new();
3963 vik_track_set_defaults ( vtl->current_track );
3964 vtl->current_track->visible = TRUE;
3965 vtl->current_track->is_route = TRUE;
3966 // By default make all routes red
3967 vtl->current_track->has_color = TRUE;
3968 gdk_color_parse ( "red", &vtl->current_track->color );
3969 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3972 static void trw_layer_new_route ( menu_array_layer values )
3974 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3976 if ( ! vtl->current_track ) {
3977 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3978 new_route_create_common ( vtl, name );
3980 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3984 static void trw_layer_auto_routes_view ( menu_array_layer values )
3986 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3987 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3989 if ( g_hash_table_size (vtl->routes) > 0 ) {
3990 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3991 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3992 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3993 vik_layers_panel_emit_update ( vlp );
3998 static void trw_layer_finish_track ( menu_array_layer values )
4000 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4001 vtl->current_track = NULL;
4002 vtl->route_finder_started = FALSE;
4003 vik_layer_emit_update ( VIK_LAYER(vtl) );
4006 static void trw_layer_auto_tracks_view ( menu_array_layer values )
4008 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4009 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4011 if ( g_hash_table_size (vtl->tracks) > 0 ) {
4012 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4013 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
4014 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4015 vik_layers_panel_emit_update ( vlp );
4019 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
4021 /* NB do not care if wp is visible or not */
4022 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
4025 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
4027 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4028 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4030 /* Only 1 waypoint - jump straight to it */
4031 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
4032 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
4033 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
4035 /* If at least 2 waypoints - find center and then zoom to fit */
4036 else if ( g_hash_table_size (vtl->waypoints) > 1 )
4038 struct LatLon maxmin[2] = { {0,0}, {0,0} };
4039 maxmin[0].lat = vtl->waypoints_bbox.north;
4040 maxmin[1].lat = vtl->waypoints_bbox.south;
4041 maxmin[0].lon = vtl->waypoints_bbox.east;
4042 maxmin[1].lon = vtl->waypoints_bbox.west;
4043 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4046 vik_layers_panel_emit_update ( vlp );
4049 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
4051 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
4054 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
4056 if ( values[MA_MISC] ) {
4057 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
4058 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
4062 static GtkWidget* create_external_submenu ( GtkMenu *menu )
4064 GtkWidget *external_submenu = gtk_menu_new ();
4065 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
4066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
4067 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4068 gtk_widget_show ( item );
4069 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
4070 return external_submenu;
4073 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
4075 static menu_array_layer pass_along;
4077 GtkWidget *export_submenu;
4078 pass_along[MA_VTL] = vtl;
4079 pass_along[MA_VLP] = vlp;
4081 item = gtk_menu_item_new();
4082 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4083 gtk_widget_show ( item );
4085 if ( vtl->current_track ) {
4086 if ( vtl->current_track->is_route )
4087 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
4089 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
4090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
4091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4092 gtk_widget_show ( item );
4095 item = gtk_menu_item_new ();
4096 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4097 gtk_widget_show ( item );
4100 /* Now with icons */
4101 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4104 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4105 gtk_widget_show ( item );
4107 GtkWidget *view_submenu = gtk_menu_new();
4108 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4109 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4110 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4111 gtk_widget_show ( item );
4112 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4114 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4117 gtk_widget_show ( item );
4119 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4121 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4122 gtk_widget_show ( item );
4124 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4125 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4126 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4127 gtk_widget_show ( item );
4129 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4132 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4133 gtk_widget_show ( item );
4135 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4136 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4137 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4138 gtk_widget_show ( item );
4140 export_submenu = gtk_menu_new ();
4141 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4142 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4143 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4144 gtk_widget_show ( item );
4145 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4147 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4148 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4149 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4150 gtk_widget_show ( item );
4152 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4155 gtk_widget_show ( item );
4157 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4158 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4159 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4160 gtk_widget_show ( item );
4162 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4164 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4165 gtk_widget_show ( item );
4167 if ( have_geojson_export ) {
4168 item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4169 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4170 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4171 gtk_widget_show ( item );
4174 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4175 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4176 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4177 gtk_widget_show ( item );
4179 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4180 item = gtk_menu_item_new_with_mnemonic ( external1 );
4181 g_free ( external1 );
4182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4183 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4184 gtk_widget_show ( item );
4186 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4187 item = gtk_menu_item_new_with_mnemonic ( external2 );
4188 g_free ( external2 );
4189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4190 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4191 gtk_widget_show ( item );
4193 GtkWidget *new_submenu = gtk_menu_new();
4194 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4195 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4196 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4197 gtk_widget_show(item);
4198 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4200 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4201 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4202 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4203 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4204 gtk_widget_show ( item );
4206 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4209 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4210 gtk_widget_show ( item );
4211 // Make it available only when a new track *not* already in progress
4212 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4214 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4215 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4216 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4217 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4218 gtk_widget_show ( item );
4219 // Make it available only when a new track *not* already in progress
4220 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4222 #ifdef VIK_CONFIG_GEOTAG
4223 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4225 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4226 gtk_widget_show ( item );
4229 GtkWidget *acquire_submenu = gtk_menu_new ();
4230 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4231 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4232 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4233 gtk_widget_show ( item );
4234 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4236 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4237 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4238 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4239 gtk_widget_show ( item );
4241 /* FIXME: only add menu when at least a routing engine has support for Directions */
4242 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4244 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4245 gtk_widget_show ( item );
4247 #ifdef VIK_CONFIG_OPENSTREETMAP
4248 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4250 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4251 gtk_widget_show ( item );
4253 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4255 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4256 gtk_widget_show ( item );
4259 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4261 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4262 gtk_widget_show ( item );
4264 #ifdef VIK_CONFIG_GEONAMES
4265 GtkWidget *wikipedia_submenu = gtk_menu_new();
4266 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4267 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4268 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4269 gtk_widget_show(item);
4270 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4272 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4273 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4274 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4275 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4276 gtk_widget_show ( item );
4278 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4279 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4280 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4281 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4282 gtk_widget_show ( item );
4285 #ifdef VIK_CONFIG_GEOCACHES
4286 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4288 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4289 gtk_widget_show ( item );
4292 #ifdef VIK_CONFIG_GEOTAG
4293 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4294 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4295 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4296 gtk_widget_show ( item );
4299 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4300 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4301 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4302 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4303 gtk_widget_show ( item );
4305 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4307 GtkWidget *upload_submenu = gtk_menu_new ();
4308 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4309 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4310 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4311 gtk_widget_show ( item );
4312 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4314 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4315 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4317 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4318 gtk_widget_show ( item );
4320 #ifdef VIK_CONFIG_OPENSTREETMAP
4321 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4322 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4323 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4324 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4325 gtk_widget_show ( item );
4328 GtkWidget *delete_submenu = gtk_menu_new ();
4329 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4330 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4331 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4332 gtk_widget_show ( item );
4333 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4335 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4336 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4337 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4338 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4339 gtk_widget_show ( item );
4341 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4342 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4344 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4345 gtk_widget_show ( item );
4347 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4348 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4349 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4350 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4351 gtk_widget_show ( item );
4353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4355 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4356 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4357 gtk_widget_show ( item );
4359 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4360 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4361 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4362 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4363 gtk_widget_show ( item );
4365 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4366 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4367 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4368 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4369 gtk_widget_show ( item );
4371 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4372 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4374 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4375 gtk_widget_show ( item );
4378 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4379 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4381 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4382 gtk_widget_show ( item );
4385 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4386 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4387 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4388 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4389 gtk_widget_show ( item );
4390 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4392 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4393 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4395 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4396 gtk_widget_show ( item );
4397 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4399 GtkWidget *external_submenu = create_external_submenu ( menu );
4400 // TODO: Should use selected layer's centre - rather than implicitly using the current viewport
4401 vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (external_submenu), NULL );
4404 // Fake Waypoint UUIDs vi simple increasing integer
4405 static guint wp_uuid = 0;
4407 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4411 vik_waypoint_set_name (wp, name);
4413 if ( VIK_LAYER(vtl)->realized )
4415 // Do we need to create the sublayer:
4416 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4417 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4420 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4422 time_t timestamp = 0;
4423 if ( wp->has_timestamp )
4424 timestamp = wp->timestamp;
4426 // Visibility column always needed for waypoints
4427 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 );
4429 // Actual setting of visibility dependent on the waypoint
4430 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4432 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4434 // Sort now as post_read is not called on a realized waypoint
4435 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4438 highest_wp_number_add_wp(vtl, name);
4439 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4443 // Fake Track UUIDs vi simple increasing integer
4444 static guint tr_uuid = 0;
4446 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4450 vik_track_set_name (t, name);
4452 if ( VIK_LAYER(vtl)->realized )
4454 // Do we need to create the sublayer:
4455 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4456 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4459 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4461 time_t timestamp = 0;
4462 VikTrackpoint *tpt = vik_track_get_tp_first(t);
4463 if ( tpt && tpt->has_timestamp )
4464 timestamp = tpt->timestamp;
4466 // Visibility column always needed for tracks
4467 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 );
4469 // Actual setting of visibility dependent on the track
4470 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4472 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4474 // Sort now as post_read is not called on a realized track
4475 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4478 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4480 trw_layer_update_treeview ( vtl, t );
4483 // Fake Route UUIDs vi simple increasing integer
4484 static guint rt_uuid = 0;
4486 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4490 vik_track_set_name (t, name);
4492 if ( VIK_LAYER(vtl)->realized )
4494 // Do we need to create the sublayer:
4495 if ( g_hash_table_size (vtl->routes) == 0 ) {
4496 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4499 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4500 // Visibility column always needed for routes
4501 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
4502 // Actual setting of visibility dependent on the route
4503 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4505 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4507 // Sort now as post_read is not called on a realized route
4508 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4511 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4513 trw_layer_update_treeview ( vtl, t );
4516 /* to be called whenever a track has been deleted or may have been changed. */
4517 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4519 if (vtl->current_tp_track == trk )
4520 trw_layer_cancel_current_tp ( vtl, FALSE );
4524 * Normally this is done to due the waypoint size preference having changed
4526 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4528 GHashTableIter iter;
4529 gpointer key, value;
4532 g_hash_table_iter_init ( &iter, vtl->waypoints );
4533 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4534 VikWaypoint *wp = VIK_WAYPOINT(value);
4536 // Reapply symbol setting to update the pixbuf
4537 gchar *tmp_symbol = g_strdup ( wp->symbol );
4538 vik_waypoint_set_symbol ( wp, tmp_symbol );
4539 g_free ( tmp_symbol );
4545 * trw_layer_new_unique_sublayer_name:
4547 * Allocates a unique new name
4549 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4552 gchar *newname = g_strdup(name);
4557 switch ( sublayer_type ) {
4558 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4559 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4561 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4562 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4565 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4568 // If found a name already in use try adding 1 to it and we try again
4570 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4572 newname = new_newname;
4575 } while ( id != NULL);
4580 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4582 // No more uniqueness of name forced when loading from a file
4583 // This now makes this function a little redunant as we just flow the parameters through
4584 vik_trw_layer_add_waypoint ( vtl, name, wp );
4587 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4589 if ( vtl->route_finder_append && vtl->current_track ) {
4590 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4592 // enforce end of current track equal to start of tr
4593 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4594 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4595 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4596 vik_track_add_trackpoint ( vtl->current_track,
4597 vik_trackpoint_copy ( cur_end ),
4601 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4602 vik_track_free ( tr );
4603 vtl->route_finder_append = FALSE; /* this means we have added it */
4606 // No more uniqueness of name forced when loading from a file
4608 vik_trw_layer_add_route ( vtl, name, tr );
4610 vik_trw_layer_add_track ( vtl, name, tr );
4612 if ( vtl->route_finder_check_added_track ) {
4613 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4614 vtl->route_finder_added_track = tr;
4619 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4621 *l = g_list_append(*l, id);
4625 * Move an item from one TRW layer to another TRW layer
4627 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4629 // TODO reconsider strategy when moving within layer (if anything...)
4630 gboolean rename = ( vtl_src != vtl_dest );
4634 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4635 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4639 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4641 newname = g_strdup ( trk->name );
4643 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4644 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4646 vik_trw_layer_delete_track ( vtl_src, trk );
4647 // Reset layer timestamps in case they have now changed
4648 vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4649 vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4652 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4653 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4657 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4659 newname = g_strdup ( trk->name );
4661 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4662 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4664 vik_trw_layer_delete_route ( vtl_src, trk );
4667 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4668 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4672 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4674 newname = g_strdup ( wp->name );
4676 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4677 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4679 trw_layer_delete_waypoint ( vtl_src, wp );
4681 // Recalculate bounds even if not renamed as maybe dragged between layers
4682 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4683 trw_layer_calculate_bounds_waypoints ( vtl_src );
4684 // Reset layer timestamps in case they have now changed
4685 vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4686 vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4690 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4692 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4693 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4695 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4696 GList *items = NULL;
4699 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4700 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4702 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4703 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4705 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4706 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4711 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4712 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4713 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4714 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4716 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4723 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4724 trw_layer_move_item(vtl_src, vtl_dest, name, type);
4728 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4730 trku_udata *user_data = udata;
4731 if ( trk == user_data->trk ) {
4732 user_data->uuid = id;
4738 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4740 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;
4748 vtl->route_finder_started = FALSE;
4751 was_visible = trk->visible;
4753 if ( trk == vtl->route_finder_added_track )
4754 vtl->route_finder_added_track = NULL;
4760 // Hmmm, want key of it
4761 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4763 if ( trkf && udata.uuid ) {
4764 /* could be current_tp, so we have to check */
4765 trw_layer_cancel_tps_of_track ( vtl, trk );
4767 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4770 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4771 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4772 g_hash_table_remove ( vtl->tracks, udata.uuid );
4774 // If last sublayer, then remove sublayer container
4775 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4776 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4779 // Incase it was selected (no item delete signal ATM)
4780 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4786 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4788 gboolean was_visible = FALSE;
4790 if ( trk && trk->name ) {
4792 if ( trk == vtl->current_track ) {
4793 vtl->current_track = NULL;
4794 vtl->current_tp_track = NULL;
4795 vtl->current_tp_id = NULL;
4796 vtl->moving_tp = FALSE;
4799 was_visible = trk->visible;
4801 if ( trk == vtl->route_finder_added_track )
4802 vtl->route_finder_added_track = NULL;
4808 // Hmmm, want key of it
4809 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4811 if ( trkf && udata.uuid ) {
4812 /* could be current_tp, so we have to check */
4813 trw_layer_cancel_tps_of_track ( vtl, trk );
4815 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4818 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4819 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4820 g_hash_table_remove ( vtl->routes, udata.uuid );
4822 // If last sublayer, then remove sublayer container
4823 if ( g_hash_table_size (vtl->routes) == 0 ) {
4824 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4827 // Incase it was selected (no item delete signal ATM)
4828 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4834 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4836 gboolean was_visible = FALSE;
4838 if ( wp && wp->name ) {
4840 if ( wp == vtl->current_wp ) {
4841 vtl->current_wp = NULL;
4842 vtl->current_wp_id = NULL;
4843 vtl->moving_wp = FALSE;
4846 was_visible = wp->visible;
4852 // Hmmm, want key of it
4853 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4855 if ( wpf && udata.uuid ) {
4856 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4859 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4860 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4862 highest_wp_number_remove_wp(vtl, wp->name);
4863 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4865 // If last sublayer, then remove sublayer container
4866 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4867 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4870 // Incase it was selected (no item delete signal ATM)
4871 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4879 // Only for temporary use by trw_layer_delete_waypoint_by_name
4880 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4882 wpu_udata *user_data = udata;
4883 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4884 user_data->uuid = id;
4891 * Delete a waypoint by the given name
4892 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4893 * as there be multiple waypoints with the same name
4895 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4898 // Fake a waypoint with the given name
4899 udata.wp = vik_waypoint_new ();
4900 vik_waypoint_set_name (udata.wp, name);
4901 // Currently only the name is used in this waypoint find function
4904 // Hmmm, want key of it
4905 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4907 vik_waypoint_free (udata.wp);
4909 if ( wpf && udata.uuid )
4910 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4916 VikTrack *trk; // input
4917 gpointer uuid; // output
4920 // Only for temporary use by trw_layer_delete_track_by_name
4921 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4923 tpu_udata *user_data = udata;
4924 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4925 user_data->uuid = id;
4932 * Delete a track by the given name
4933 * NOTE: ATM this will delete the first encountered Track with the specified name
4934 * as there may be multiple tracks with the same name within the specified hash table
4936 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4939 // Fake a track with the given name
4940 udata.trk = vik_track_new ();
4941 vik_track_set_name (udata.trk, name);
4942 // Currently only the name is used in this waypoint find function
4945 // Hmmm, want key of it
4946 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4948 vik_track_free (udata.trk);
4950 if ( trkf && udata.uuid ) {
4951 // This could be a little better written...
4952 if ( vtl->tracks == ht_tracks )
4953 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4954 if ( vtl->routes == ht_tracks )
4955 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4962 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4964 vik_treeview_item_delete (vt, it );
4967 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4970 vtl->current_track = NULL;
4971 vtl->route_finder_added_track = NULL;
4972 if (vtl->current_tp_track)
4973 trw_layer_cancel_current_tp(vtl, FALSE);
4975 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4976 g_hash_table_remove_all(vtl->routes_iters);
4977 g_hash_table_remove_all(vtl->routes);
4979 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4981 vik_layer_emit_update ( VIK_LAYER(vtl) );
4984 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4987 vtl->current_track = NULL;
4988 vtl->route_finder_added_track = NULL;
4989 if (vtl->current_tp_track)
4990 trw_layer_cancel_current_tp(vtl, FALSE);
4992 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4993 g_hash_table_remove_all(vtl->tracks_iters);
4994 g_hash_table_remove_all(vtl->tracks);
4996 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4998 vik_layer_emit_update ( VIK_LAYER(vtl) );
5001 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
5003 vtl->current_wp = NULL;
5004 vtl->current_wp_id = NULL;
5005 vtl->moving_wp = FALSE;
5007 highest_wp_number_reset(vtl);
5009 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5010 g_hash_table_remove_all(vtl->waypoints_iters);
5011 g_hash_table_remove_all(vtl->waypoints);
5013 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
5015 vik_layer_emit_update ( VIK_LAYER(vtl) );
5018 static void trw_layer_delete_all_tracks ( menu_array_layer values )
5020 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5021 // Get confirmation from the user
5022 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5023 _("Are you sure you want to delete all tracks in %s?"),
5024 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5025 vik_trw_layer_delete_all_tracks (vtl);
5028 static void trw_layer_delete_all_routes ( menu_array_layer values )
5030 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5031 // Get confirmation from the user
5032 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5033 _("Are you sure you want to delete all routes in %s?"),
5034 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5035 vik_trw_layer_delete_all_routes (vtl);
5038 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
5040 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5041 // Get confirmation from the user
5042 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5043 _("Are you sure you want to delete all waypoints in %s?"),
5044 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5045 vik_trw_layer_delete_all_waypoints (vtl);
5048 static void trw_layer_delete_item ( menu_array_sublayer values )
5050 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5051 gboolean was_visible = FALSE;
5052 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5054 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5055 if ( wp && wp->name ) {
5056 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5057 // Get confirmation from the user
5058 // Maybe this Waypoint Delete should be optional as is it could get annoying...
5059 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5060 _("Are you sure you want to delete the waypoint \"%s\"?"),
5063 was_visible = trw_layer_delete_waypoint ( vtl, wp );
5064 trw_layer_calculate_bounds_waypoints ( vtl );
5065 // Reset layer timestamp in case it has now changed
5066 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5069 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5071 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5072 if ( trk && trk->name ) {
5073 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5074 // Get confirmation from the user
5075 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5076 _("Are you sure you want to delete the track \"%s\"?"),
5079 was_visible = vik_trw_layer_delete_track ( vtl, trk );
5080 // Reset layer timestamp in case it has now changed
5081 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5086 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5087 if ( trk && trk->name ) {
5088 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5089 // Get confirmation from the user
5090 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5091 _("Are you sure you want to delete the route \"%s\"?"),
5094 was_visible = vik_trw_layer_delete_route ( vtl, trk );
5098 vik_layer_emit_update ( VIK_LAYER(vtl) );
5102 * Rename waypoint and maintain corresponding name of waypoint in the treeview
5104 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
5106 vik_waypoint_set_name ( wp, new_name );
5108 // Now update the treeview as well
5113 // Need key of it for treeview update
5114 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5116 if ( wpf && udataU.uuid ) {
5117 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5120 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
5121 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
5127 * Maintain icon of waypoint in the treeview
5129 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5131 // update the treeview
5136 // Need key of it for treeview update
5137 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5139 if ( wpf && udataU.uuid ) {
5140 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5143 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5148 static void trw_layer_properties_item ( menu_array_sublayer values )
5150 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5151 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5153 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5155 if ( wp && wp->name )
5157 gboolean updated = FALSE;
5158 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5160 trw_layer_waypoint_rename ( vtl, wp, new_name );
5162 if ( updated && values[MA_TV_ITER] )
5163 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5165 if ( updated && VIK_LAYER(vtl)->visible )
5166 vik_layer_emit_update ( VIK_LAYER(vtl) );
5172 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5173 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5175 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5177 if ( tr && tr->name )
5179 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5190 * trw_layer_track_statistics:
5192 * Show track statistics.
5193 * ATM jump to the stats page in the properties
5194 * TODO: consider separating the stats into an individual dialog?
5196 static void trw_layer_track_statistics ( menu_array_sublayer values )
5198 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5200 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5201 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5203 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5205 if ( trk && trk->name ) {
5206 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5216 * Update the treeview of the track id - primarily to update the icon
5218 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5224 gpointer *trkf = NULL;
5225 if ( trk->is_route )
5226 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5228 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5230 if ( trkf && udata.uuid ) {
5232 GtkTreeIter *iter = NULL;
5233 if ( trk->is_route )
5234 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5236 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5239 // TODO: Make this a function
5240 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5241 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5242 ((trk->color.green & 0xff00) << 8) |
5243 (trk->color.blue & 0xff00);
5244 gdk_pixbuf_fill ( pixbuf, pixel );
5245 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5246 g_object_unref (pixbuf);
5253 Parameter 1 -> VikLayersPanel
5254 Parameter 2 -> VikLayer
5255 Parameter 3 -> VikViewport
5257 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5260 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5261 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5264 /* since vlp not set, vl & vvp should be valid instead! */
5266 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5267 vik_layer_emit_update ( VIK_LAYER(vl) );
5272 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5274 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5276 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5277 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5279 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5281 if ( track && track->trackpoints )
5282 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5285 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5287 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5289 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5290 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5292 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5294 if ( track && track->trackpoints )
5296 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5298 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5299 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5300 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5301 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5302 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5306 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5308 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5310 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5311 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5313 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5318 // Converting a track to a route can be a bit more complicated,
5319 // so give a chance to change our minds:
5320 if ( !trk->is_route &&
5321 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5322 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5324 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5325 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5330 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5333 trk_copy->is_route = !trk_copy->is_route;
5335 // ATM can't set name to self - so must create temporary copy
5336 gchar *name = g_strdup ( trk_copy->name );
5338 // Delete old one and then add new one
5339 if ( trk->is_route ) {
5340 vik_trw_layer_delete_route ( vtl, trk );
5341 vik_trw_layer_add_track ( vtl, name, trk_copy );
5344 // Extra route conversion bits...
5345 vik_track_merge_segments ( trk_copy );
5346 vik_track_to_routepoints ( trk_copy );
5348 vik_trw_layer_delete_track ( vtl, trk );
5349 vik_trw_layer_add_route ( vtl, name, trk_copy );
5353 // Update in case color of track / route changes when moving between sublayers
5354 vik_layer_emit_update ( VIK_LAYER(vtl) );
5357 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5359 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5361 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5362 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5364 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5367 vik_track_anonymize_times ( track );
5370 static void trw_layer_interpolate_times ( menu_array_sublayer values )
5372 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5374 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5375 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5377 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5380 vik_track_interpolate_times ( track );
5383 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5385 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5387 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5388 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5390 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5395 vtl->current_track = track;
5396 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);
5398 if ( track->trackpoints )
5399 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5403 * extend a track using route finder
5405 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5407 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5408 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5412 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5413 vtl->current_track = track;
5414 vtl->route_finder_started = TRUE;
5416 if ( track->trackpoints )
5417 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5423 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5425 // If have a vlp then perform a basic test to see if any DEM info available...
5427 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5429 if ( !g_list_length(dems) ) {
5430 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5438 * apply_dem_data_common:
5440 * A common function for applying the DEM values and reporting the results.
5442 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5444 if ( !trw_layer_dem_test ( vtl, vlp ) )
5447 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5448 // Inform user how much was changed
5450 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5451 g_snprintf(str, 64, tmp_str, changed);
5452 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5455 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5457 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5459 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5460 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5462 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5465 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5468 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5470 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5472 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5473 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5475 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5478 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5484 * A common function for applying the elevation smoothing and reporting the results.
5486 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5488 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5489 // Inform user how much was changed
5491 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5492 g_snprintf(str, 64, tmp_str, changed);
5493 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5499 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5501 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5503 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5504 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5506 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5511 smooth_it ( vtl, track, FALSE );
5514 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5516 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5518 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5519 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5521 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5526 smooth_it ( vtl, track, TRUE );
5530 * Commonal helper function
5532 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5535 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5536 g_snprintf(str, 64, tmp_str, changed);
5537 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5540 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5542 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5543 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5545 if ( !trw_layer_dem_test ( vtl, vlp ) )
5549 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5551 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5553 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5557 GHashTableIter iter;
5558 gpointer key, value;
5560 g_hash_table_iter_init ( &iter, vtl->waypoints );
5561 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5562 VikWaypoint *wp = VIK_WAYPOINT(value);
5563 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5566 wp_changed_message ( vtl, changed );
5569 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5571 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5572 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5574 if ( !trw_layer_dem_test ( vtl, vlp ) )
5578 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5580 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5582 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5586 GHashTableIter iter;
5587 gpointer key, value;
5589 g_hash_table_iter_init ( &iter, vtl->waypoints );
5590 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5591 VikWaypoint *wp = VIK_WAYPOINT(value);
5592 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5595 wp_changed_message ( vtl, changed );
5598 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5600 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5602 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5603 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5605 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5609 if ( !track->trackpoints )
5611 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5614 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5616 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5618 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5619 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5621 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5626 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5629 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5632 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5634 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5636 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5637 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5639 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5644 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5647 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5650 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5652 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5654 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5655 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5657 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5662 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5665 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5669 * Automatically change the viewport to center on the track and zoom to see the extent of the track
5671 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5673 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5675 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5676 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5678 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5680 if ( trk && trk->trackpoints )
5682 struct LatLon maxmin[2] = { {0,0}, {0,0} };
5683 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5684 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5685 if ( values[MA_VLP] )
5686 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5688 vik_layer_emit_update ( VIK_LAYER(vtl) );
5693 * Refine the selected track/route with a routing engine.
5694 * The routing engine is selected by the user, when requestiong the job.
5696 static void trw_layer_route_refine ( menu_array_sublayer values )
5698 static gint last_engine = 0;
5699 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5702 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5703 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5705 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5707 if ( trk && trk->trackpoints )
5709 /* Check size of the route */
5710 int nb = vik_track_get_tp_count(trk);
5712 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5713 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5714 GTK_MESSAGE_WARNING,
5715 GTK_BUTTONS_OK_CANCEL,
5716 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5718 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5719 gtk_widget_destroy ( dialog );
5720 if (response != GTK_RESPONSE_OK )
5723 /* Select engine from dialog */
5724 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5725 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5726 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5728 GTK_RESPONSE_REJECT,
5730 GTK_RESPONSE_ACCEPT,
5732 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5733 gtk_widget_show_all(label);
5735 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5737 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5738 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5739 gtk_widget_show_all(combo);
5741 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5743 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5745 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5747 /* Dialog validated: retrieve selected engine and do the job */
5748 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5749 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5752 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5754 /* Force saving track */
5755 /* FIXME: remove or rename this hack */
5756 vtl->route_finder_check_added_track = TRUE;
5759 vik_routing_engine_refine (routing, vtl, trk);
5761 /* FIXME: remove or rename this hack */
5762 if ( vtl->route_finder_added_track )
5763 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5765 vtl->route_finder_added_track = NULL;
5766 vtl->route_finder_check_added_track = FALSE;
5768 vik_layer_emit_update ( VIK_LAYER(vtl) );
5770 /* Restore cursor */
5771 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5773 gtk_widget_destroy ( dialog );
5777 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5779 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5780 trw_layer_tpwin_init ( vtl );
5783 /*************************************
5784 * merge/split by time routines
5785 *************************************/
5787 /* called for each key in track hash table.
5788 * If the current track has the same time stamp type, add it to the result,
5789 * except the one pointed by "exclude".
5790 * set exclude to NULL if there is no exclude to check.
5791 * Note that the result is in reverse (for performance reasons).
5796 gboolean with_timestamps;
5798 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5800 twt_udata *user_data = udata;
5801 VikTrackpoint *p1, *p2;
5802 VikTrack *trk = VIK_TRACK(value);
5803 if (trk == user_data->exclude) {
5807 if (trk->trackpoints) {
5808 p1 = vik_track_get_tp_first(trk);
5809 p2 = vik_track_get_tp_last(trk);
5811 if ( user_data->with_timestamps ) {
5812 if (!p1->has_timestamp || !p2->has_timestamp) {
5817 // Don't add tracks with timestamps when getting non timestamp tracks
5818 if (p1->has_timestamp || p2->has_timestamp) {
5824 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5828 * find_nearby_tracks_by_time:
5830 * Called for each track in track hash table.
5831 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5832 * to the current track, then the current track is added to the list in user_data[0]
5834 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5836 VikTrack *trk = VIK_TRACK(value);
5838 GList **nearby_tracks = ((gpointer *)user_data)[0];
5839 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5841 if ( !orig_trk || !orig_trk->trackpoints )
5845 * detect reasons for not merging, and return
5846 * if no reason is found not to merge, then do it.
5849 twt_udata *udata = user_data;
5850 // Exclude the original track from the compiled list
5851 if (trk == udata->exclude) {
5855 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5856 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5858 if (trk->trackpoints) {
5860 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5861 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5863 if (!p1->has_timestamp || !p2->has_timestamp) {
5864 //g_print("no timestamp\n");
5868 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5869 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5870 if (! (labs(t1 - p2->timestamp) < threshold ||
5872 labs(p1->timestamp - t2) < threshold)
5879 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5882 /* comparison function used to sort tracks; a and b are hash table keys */
5883 /* Not actively used - can be restored if needed
5884 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5886 GHashTable *tracks = user_data;
5889 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5890 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5892 if (t1 < t2) return -1;
5893 if (t1 > t2) return 1;
5898 /* comparison function used to sort trackpoints */
5899 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5901 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5903 if (t1 < t2) return -1;
5904 if (t1 > t2) return 1;
5909 * comparison function which can be used to sort tracks or waypoints by name
5911 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5913 const gchar* namea = (const gchar*) a;
5914 const gchar* nameb = (const gchar*) b;
5915 if ( namea == NULL || nameb == NULL)
5918 // Same sort method as used in the vik_treeview_*_alphabetize functions
5919 return strcmp ( namea, nameb );
5923 * Attempt to merge selected track with other tracks specified by the user
5924 * Tracks to merge with must be of the same 'type' as the selected track -
5925 * either all with timestamps, or all without timestamps
5927 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5929 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5930 GList *other_tracks = NULL;
5931 GHashTable *ght_tracks;
5932 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5933 ght_tracks = vtl->routes;
5935 ght_tracks = vtl->tracks;
5937 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5942 if ( !track->trackpoints )
5946 udata.result = &other_tracks;
5947 udata.exclude = track;
5948 // Allow merging with 'similar' time type time tracks
5949 // i.e. either those times, or those without
5950 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5952 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5953 other_tracks = g_list_reverse(other_tracks);
5955 if ( !other_tracks ) {
5956 if ( udata.with_timestamps )
5957 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5959 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5963 // Sort alphabetically for user presentation
5964 // Convert into list of names for usage with dialog function
5965 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5966 GList *other_tracks_names = NULL;
5967 GList *iter = g_list_first ( other_tracks );
5969 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5970 iter = g_list_next ( iter );
5973 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5975 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5979 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5980 g_list_free(other_tracks);
5981 g_list_free(other_tracks_names);
5986 for (l = merge_list; l != NULL; l = g_list_next(l)) {
5987 VikTrack *merge_track;
5988 if ( track->is_route )
5989 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5991 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5994 vik_track_steal_and_append_trackpoints ( track, merge_track );
5995 if ( track->is_route )
5996 vik_trw_layer_delete_route (vtl, merge_track);
5998 vik_trw_layer_delete_track (vtl, merge_track);
5999 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
6002 for (l = merge_list; l != NULL; l = g_list_next(l))
6004 g_list_free(merge_list);
6006 vik_layer_emit_update( VIK_LAYER(vtl) );
6010 // c.f. trw_layer_sorted_track_id_by_name_list
6011 // but don't add the specified track to the list (normally current track)
6012 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
6014 twt_udata *user_data = udata;
6017 if (trk == user_data->exclude) {
6021 // Sort named list alphabetically
6022 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
6026 * Join - this allows combining 'tracks' and 'track routes'
6027 * i.e. doesn't care about whether tracks have consistent timestamps
6028 * ATM can only append one track at a time to the currently selected track
6030 static void trw_layer_append_track ( menu_array_sublayer values )
6033 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6035 GHashTable *ght_tracks;
6036 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6037 ght_tracks = vtl->routes;
6039 ght_tracks = vtl->tracks;
6041 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
6046 GList *other_tracks_names = NULL;
6048 // Sort alphabetically for user presentation
6049 // Convert into list of names for usage with dialog function
6050 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6052 udata.result = &other_tracks_names;
6053 udata.exclude = trk;
6055 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6057 // Note the limit to selecting one track only
6058 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6059 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6060 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6063 trk->is_route ? _("Append Route"): _("Append Track"),
6064 trk->is_route ? _("Select the route to append after the current route") :
6065 _("Select the track to append after the current track") );
6067 g_list_free(other_tracks_names);
6069 // It's a list, but shouldn't contain more than one other track!
6070 if ( append_list ) {
6072 for (l = append_list; l != NULL; l = g_list_next(l)) {
6073 // TODO: at present this uses the first track found by name,
6074 // which with potential multiple same named tracks may not be the one selected...
6075 VikTrack *append_track;
6076 if ( trk->is_route )
6077 append_track = vik_trw_layer_get_route ( vtl, l->data );
6079 append_track = vik_trw_layer_get_track ( vtl, l->data );
6081 if ( append_track ) {
6082 vik_track_steal_and_append_trackpoints ( trk, append_track );
6083 if ( trk->is_route )
6084 vik_trw_layer_delete_route (vtl, append_track);
6086 vik_trw_layer_delete_track (vtl, append_track);
6089 for (l = append_list; l != NULL; l = g_list_next(l))
6091 g_list_free(append_list);
6093 vik_layer_emit_update( VIK_LAYER(vtl) );
6098 * Very similar to trw_layer_append_track for joining
6099 * but this allows selection from the 'other' list
6100 * If a track is selected, then is shows routes and joins the selected one
6101 * If a route is selected, then is shows tracks and joins the selected one
6103 static void trw_layer_append_other ( menu_array_sublayer values )
6106 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6108 GHashTable *ght_mykind, *ght_others;
6109 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6110 ght_mykind = vtl->routes;
6111 ght_others = vtl->tracks;
6114 ght_mykind = vtl->tracks;
6115 ght_others = vtl->routes;
6118 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6123 GList *other_tracks_names = NULL;
6125 // Sort alphabetically for user presentation
6126 // Convert into list of names for usage with dialog function
6127 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6129 udata.result = &other_tracks_names;
6130 udata.exclude = trk;
6132 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6134 // Note the limit to selecting one track only
6135 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6136 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6137 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6140 trk->is_route ? _("Append Track"): _("Append Route"),
6141 trk->is_route ? _("Select the track to append after the current route") :
6142 _("Select the route to append after the current track") );
6144 g_list_free(other_tracks_names);
6146 // It's a list, but shouldn't contain more than one other track!
6147 if ( append_list ) {
6149 for (l = append_list; l != NULL; l = g_list_next(l)) {
6150 // TODO: at present this uses the first track found by name,
6151 // which with potential multiple same named tracks may not be the one selected...
6153 // Get FROM THE OTHER TYPE list
6154 VikTrack *append_track;
6155 if ( trk->is_route )
6156 append_track = vik_trw_layer_get_track ( vtl, l->data );
6158 append_track = vik_trw_layer_get_route ( vtl, l->data );
6160 if ( append_track ) {
6162 if ( !append_track->is_route &&
6163 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6164 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6166 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6167 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6168 vik_track_merge_segments ( append_track );
6169 vik_track_to_routepoints ( append_track );
6176 vik_track_steal_and_append_trackpoints ( trk, append_track );
6178 // Delete copied which is FROM THE OTHER TYPE list
6179 if ( trk->is_route )
6180 vik_trw_layer_delete_track (vtl, append_track);
6182 vik_trw_layer_delete_route (vtl, append_track);
6185 for (l = append_list; l != NULL; l = g_list_next(l))
6187 g_list_free(append_list);
6188 vik_layer_emit_update( VIK_LAYER(vtl) );
6192 /* merge by segments */
6193 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6195 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6196 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6197 guint segments = vik_track_merge_segments ( trk );
6198 // NB currently no need to redraw as segments not actually shown on the display
6199 // However inform the user of what happened:
6201 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6202 g_snprintf(str, 64, tmp_str, segments);
6203 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6206 /* merge by time routine */
6207 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6209 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6213 GList *tracks_with_timestamp = NULL;
6214 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6215 if (orig_trk->trackpoints &&
6216 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6217 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6222 udata.result = &tracks_with_timestamp;
6223 udata.exclude = orig_trk;
6224 udata.with_timestamps = TRUE;
6225 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6226 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6228 if (!tracks_with_timestamp) {
6229 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6232 g_list_free(tracks_with_timestamp);
6234 static guint threshold_in_minutes = 1;
6235 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6236 _("Merge Threshold..."),
6237 _("Merge when time between tracks less than:"),
6238 &threshold_in_minutes)) {
6242 // keep attempting to merge all tracks until no merges within the time specified is possible
6243 gboolean attempt_merge = TRUE;
6244 GList *nearby_tracks = NULL;
6246 static gpointer params[3];
6248 while ( attempt_merge ) {
6250 // Don't try again unless tracks have changed
6251 attempt_merge = FALSE;
6253 trps = orig_trk->trackpoints;
6257 if (nearby_tracks) {
6258 g_list_free(nearby_tracks);
6259 nearby_tracks = NULL;
6262 params[0] = &nearby_tracks;
6263 params[1] = orig_trk;
6264 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6266 /* get a list of adjacent-in-time tracks */
6267 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6270 GList *l = nearby_tracks;
6272 /* remove trackpoints from merged track, delete track */
6273 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6274 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6276 // Tracks have changed, therefore retry again against all the remaining tracks
6277 attempt_merge = TRUE;
6282 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6285 g_list_free(nearby_tracks);
6287 vik_layer_emit_update( VIK_LAYER(vtl) );
6291 * Split a track at the currently selected trackpoint
6293 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6295 if ( !vtl->current_tpl )
6298 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6299 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6301 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6302 GList *newglist = g_list_alloc ();
6303 newglist->prev = NULL;
6304 newglist->next = vtl->current_tpl->next;
6305 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6306 tr->trackpoints = newglist;
6308 vtl->current_tpl->next->prev = newglist; /* end old track here */
6309 vtl->current_tpl->next = NULL;
6311 // Bounds of the selected track changed due to the split
6312 vik_track_calculate_bounds ( vtl->current_tp_track );
6314 vtl->current_tpl = newglist; /* change tp to first of new track. */
6315 vtl->current_tp_track = tr;
6318 vik_trw_layer_add_route ( vtl, name, tr );
6320 vik_trw_layer_add_track ( vtl, name, tr );
6322 // Bounds of the new track created by the split
6323 vik_track_calculate_bounds ( tr );
6329 // Also need id of newly created track
6332 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6334 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6336 if ( trkf && udata.uuid )
6337 vtl->current_tp_id = udata.uuid;
6339 vtl->current_tp_id = NULL;
6341 vik_layer_emit_update(VIK_LAYER(vtl));
6347 /* split by time routine */
6348 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6350 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6351 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6352 GList *trps = track->trackpoints;
6354 GList *newlists = NULL;
6355 GList *newtps = NULL;
6356 static guint thr = 1;
6363 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6364 _("Split Threshold..."),
6365 _("Split when time between trackpoints exceeds:"),
6370 /* iterate through trackpoints, and copy them into new lists without touching original list */
6371 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6375 ts = VIK_TRACKPOINT(iter->data)->timestamp;
6377 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6380 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6381 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6382 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6384 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6389 if (ts - prev_ts > thr*60) {
6390 /* flush accumulated trackpoints into new list */
6391 newlists = g_list_append(newlists, g_list_reverse(newtps));
6395 /* accumulate trackpoint copies in newtps, in reverse order */
6396 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6398 iter = g_list_next(iter);
6401 newlists = g_list_append(newlists, g_list_reverse(newtps));
6404 /* put lists of trackpoints into tracks */
6406 // Only bother updating if the split results in new tracks
6407 if (g_list_length (newlists) > 1) {
6412 tr = vik_track_copy ( track, FALSE );
6413 tr->trackpoints = (GList *)(iter->data);
6415 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6416 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6417 g_free ( new_tr_name );
6418 vik_track_calculate_bounds ( tr );
6419 iter = g_list_next(iter);
6421 // Remove original track and then update the display
6422 vik_trw_layer_delete_track (vtl, track);
6423 vik_layer_emit_update(VIK_LAYER(vtl));
6425 g_list_free(newlists);
6429 * Split a track by the number of points as specified by the user
6431 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6433 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6435 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6436 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6438 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6443 // Check valid track
6444 GList *trps = track->trackpoints;
6448 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6449 _("Split Every Nth Point"),
6450 _("Split on every Nth point:"),
6451 250, // Default value as per typical limited track capacity of various GPS devices
6455 // Was a valid number returned?
6461 GList *newlists = NULL;
6462 GList *newtps = NULL;
6467 /* accumulate trackpoint copies in newtps, in reverse order */
6468 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6470 if (count >= points) {
6471 /* flush accumulated trackpoints into new list */
6472 newlists = g_list_append(newlists, g_list_reverse(newtps));
6476 iter = g_list_next(iter);
6479 // If there is a remaining chunk put that into the new split list
6480 // This may well be the whole track if no split points were encountered
6482 newlists = g_list_append(newlists, g_list_reverse(newtps));
6485 /* put lists of trackpoints into tracks */
6487 // Only bother updating if the split results in new tracks
6488 if (g_list_length (newlists) > 1) {
6493 tr = vik_track_copy ( track, FALSE );
6494 tr->trackpoints = (GList *)(iter->data);
6496 if ( track->is_route ) {
6497 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6498 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6501 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6502 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6504 g_free ( new_tr_name );
6505 vik_track_calculate_bounds ( tr );
6507 iter = g_list_next(iter);
6509 // Remove original track and then update the display
6510 if ( track->is_route )
6511 vik_trw_layer_delete_route (vtl, track);
6513 vik_trw_layer_delete_track (vtl, track);
6514 vik_layer_emit_update(VIK_LAYER(vtl));
6516 g_list_free(newlists);
6520 * Split a track at the currently selected trackpoint
6522 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6524 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6525 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6526 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6530 * Split a track by its segments
6531 * Routes do not have segments so don't call this for routes
6533 static void trw_layer_split_segments ( menu_array_sublayer values )
6535 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6536 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6543 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6546 for ( i = 0; i < ntracks; i++ ) {
6548 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6549 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6550 g_free ( new_tr_name );
6555 // Remove original track
6556 vik_trw_layer_delete_track ( vtl, trk );
6557 vik_layer_emit_update ( VIK_LAYER(vtl) );
6560 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6563 /* end of split/merge routines */
6565 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6569 // Find available adjacent trackpoint
6570 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6571 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6572 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6574 // Delete current trackpoint
6575 vik_trackpoint_free ( vtl->current_tpl->data );
6576 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6578 // Set to current to the available adjacent trackpoint
6579 vtl->current_tpl = new_tpl;
6581 if ( vtl->current_tp_track ) {
6582 vik_track_calculate_bounds ( vtl->current_tp_track );
6586 // Delete current trackpoint
6587 vik_trackpoint_free ( vtl->current_tpl->data );
6588 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6589 trw_layer_cancel_current_tp ( vtl, FALSE );
6594 * Delete the selected point
6596 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6598 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6600 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6601 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6603 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6608 if ( !vtl->current_tpl )
6611 trw_layer_trackpoint_selected_delete ( vtl, trk );
6613 // Track has been updated so update tps:
6614 trw_layer_cancel_tps_of_track ( vtl, trk );
6616 vik_layer_emit_update ( VIK_LAYER(vtl) );
6620 * Delete adjacent track points at the same position
6621 * AKA Delete Dulplicates on the Properties Window
6623 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6625 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6627 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6628 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6630 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6635 gulong removed = vik_track_remove_dup_points ( trk );
6637 // Track has been updated so update tps:
6638 trw_layer_cancel_tps_of_track ( vtl, trk );
6640 // Inform user how much was deleted as it's not obvious from the normal view
6642 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6643 g_snprintf(str, 64, tmp_str, removed);
6644 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6646 vik_layer_emit_update ( VIK_LAYER(vtl) );
6650 * Delete adjacent track points with the same timestamp
6651 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6653 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6655 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6657 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6658 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6660 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6665 gulong removed = vik_track_remove_same_time_points ( trk );
6667 // Track has been updated so update tps:
6668 trw_layer_cancel_tps_of_track ( vtl, trk );
6670 // Inform user how much was deleted as it's not obvious from the normal view
6672 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6673 g_snprintf(str, 64, tmp_str, removed);
6674 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6676 vik_layer_emit_update ( VIK_LAYER(vtl) );
6682 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6684 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6686 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6687 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6689 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6694 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6696 vik_layer_emit_update ( VIK_LAYER(vtl) );
6699 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6701 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6703 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6704 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6706 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6711 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6713 vik_layer_emit_update ( VIK_LAYER(vtl) );
6719 static void trw_layer_reverse ( menu_array_sublayer values )
6721 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6723 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6724 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6726 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6731 vik_track_reverse ( track );
6733 vik_layer_emit_update ( VIK_LAYER(vtl) );
6737 * Open a program at the specified date
6738 * Mainly for RedNotebook - http://rednotebook.sourceforge.net/
6739 * But could work with any program that accepts a command line of --date=<date>
6740 * FUTURE: Allow configuring of command line options + date format
6742 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6745 gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str );
6746 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6747 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program );
6748 g_error_free ( err );
6754 * Open a diary at the date of the track or waypoint
6756 static void trw_layer_diary ( menu_array_sublayer values )
6758 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6760 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6761 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6767 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6768 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6769 trw_layer_diary_open ( vtl, date_buf );
6772 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6774 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6775 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6781 if ( wpt->has_timestamp ) {
6782 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6783 trw_layer_diary_open ( vtl, date_buf );
6786 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6791 * Open a program at the specified date
6792 * Mainly for Stellarium - http://stellarium.org/
6793 * But could work with any program that accepts the same command line options...
6794 * FUTURE: Allow configuring of command line options + format or parameters
6796 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 )
6800 g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, NULL );
6801 gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
6802 astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
6803 g_warning ( "%s", cmd );
6804 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6805 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program );
6806 g_warning ( "%s", err->message );
6807 g_error_free ( err );
6809 util_add_to_deletion_list ( tmp );
6814 // Format of stellarium lat & lon seems designed to be particularly awkward
6815 // who uses ' & " in the parameters for the command line?!
6818 static gchar *convert_to_dms ( gdouble dec )
6824 gchar *result = NULL;
6838 tmp = (tmp - val_d) * 60;
6842 val_s = (tmp - val_m) * 60;
6845 result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
6850 * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
6852 static void trw_layer_astro ( menu_array_sublayer values )
6854 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6856 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6857 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6861 VikTrackpoint *tp = NULL;
6862 if ( vtl->current_tpl )
6863 // Current Trackpoint
6864 tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6865 else if ( trk->trackpoints )
6866 // Otherwise first trackpoint
6867 tp = VIK_TRACKPOINT(trk->trackpoints->data);
6872 if ( tp->has_timestamp ) {
6874 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
6876 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
6878 vik_coord_to_latlon ( &tp->coord, &ll );
6879 gchar *lat_str = convert_to_dms ( ll.lat );
6880 gchar *lon_str = convert_to_dms ( ll.lon );
6882 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) );
6883 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf);
6888 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6890 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6891 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6895 if ( wpt->has_timestamp ) {
6897 strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
6899 strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
6901 vik_coord_to_latlon ( &wpt->coord, &ll );
6902 gchar *lat_str = convert_to_dms ( ll.lat );
6903 gchar *lon_str = convert_to_dms ( ll.lon );
6905 snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) );
6906 trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf );
6911 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6916 * Similar to trw_layer_enum_item, but this uses a sorted method
6919 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6921 GList **list = (GList**)udata;
6922 // *list = g_list_prepend(*all, key); //unsorted method
6923 // Sort named list alphabetically
6924 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6929 * Now Waypoint specific sort
6931 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6933 GList **list = (GList**)udata;
6934 // Sort named list alphabetically
6935 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6939 * Track specific sort
6941 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6943 GList **list = (GList**)udata;
6944 // Sort named list alphabetically
6945 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6950 gboolean has_same_track_name;
6951 const gchar *same_track_name;
6952 } same_track_name_udata;
6954 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6956 const gchar* namea = (const gchar*) aa;
6957 const gchar* nameb = (const gchar*) bb;
6960 gint result = strcmp ( namea, nameb );
6962 if ( result == 0 ) {
6963 // Found two names the same
6964 same_track_name_udata *user_data = udata;
6965 user_data->has_same_track_name = TRUE;
6966 user_data->same_track_name = namea;
6969 // Leave ordering the same
6974 * Find out if any tracks have the same name in this hash table
6976 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6978 // Sort items by name, then compare if any next to each other are the same
6980 GList *track_names = NULL;
6981 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6984 if ( ! track_names )
6987 same_track_name_udata udata;
6988 udata.has_same_track_name = FALSE;
6990 // Use sort routine to traverse list comparing items
6991 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6992 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6993 // Still no tracks...
6997 return udata.has_same_track_name;
7001 * Force unqiue track names for the track table specified
7002 * Note the panel is a required parameter to enable the update of the names displayed
7003 * Specify if on tracks or else on routes
7005 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
7007 // . Search list for an instance of repeated name
7008 // . get track of this name
7009 // . create new name
7010 // . rename track & update equiv. treeview iter
7011 // . repeat until all different
7013 same_track_name_udata udata;
7015 GList *track_names = NULL;
7016 udata.has_same_track_name = FALSE;
7017 udata.same_track_name = NULL;
7019 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7022 if ( ! track_names )
7025 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7027 // Still no tracks...
7028 if ( ! dummy_list1 )
7031 while ( udata.has_same_track_name ) {
7033 // Find a track with the same name
7036 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
7038 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
7042 g_critical("Houston, we've had a problem.");
7043 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7044 _("Internal Error in vik_trw_layer_uniquify_tracks") );
7049 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
7050 vik_track_set_name ( trk, newname );
7056 // Need want key of it for treeview update
7057 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7059 if ( trkf && udataU.uuid ) {
7063 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7065 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7068 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
7070 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
7072 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
7076 // Start trying to find same names again...
7078 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7079 udata.has_same_track_name = FALSE;
7080 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7082 // No tracks any more - give up searching
7083 if ( ! dummy_list2 )
7084 udata.has_same_track_name = FALSE;
7088 vik_layers_panel_emit_update ( vlp );
7091 static void trw_layer_sort_order_specified ( VikTrwLayer *vtl, guint sublayer_type, vik_layer_sort_order_t order )
7095 switch (sublayer_type) {
7096 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
7097 iter = &(vtl->tracks_iter);
7098 vtl->track_sort_order = order;
7100 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7101 iter = &(vtl->routes_iter);
7102 vtl->track_sort_order = order;
7104 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7105 iter = &(vtl->waypoints_iter);
7106 vtl->wp_sort_order = order;
7110 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, order );
7113 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
7115 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7116 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_ASCENDING );
7119 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
7121 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7122 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING );
7125 static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values )
7127 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7128 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING );
7131 static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values )
7133 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7134 trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING );
7140 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
7142 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7145 // Ensure list of track names offered is unique
7146 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
7147 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7148 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7149 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
7155 // Sort list alphabetically for better presentation
7156 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7159 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
7163 // Get list of items to delete from the user
7164 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7167 _("Delete Selection"),
7168 _("Select tracks to delete"));
7171 // Delete requested tracks
7172 // since specificly requested, IMHO no need for extra confirmation
7173 if ( delete_list ) {
7175 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7176 // This deletes first trk it finds of that name (but uniqueness is enforced above)
7177 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
7179 g_list_free(delete_list);
7180 // Reset layer timestamps in case they have now changed
7181 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7183 vik_layer_emit_update( VIK_LAYER(vtl) );
7190 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
7192 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7195 // Ensure list of track names offered is unique
7196 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
7197 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7198 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7199 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
7205 // Sort list alphabetically for better presentation
7206 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7209 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
7213 // Get list of items to delete from the user
7214 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7217 _("Delete Selection"),
7218 _("Select routes to delete") );
7221 // Delete requested routes
7222 // since specificly requested, IMHO no need for extra confirmation
7223 if ( delete_list ) {
7225 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7226 // This deletes first route it finds of that name (but uniqueness is enforced above)
7227 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
7229 g_list_free(delete_list);
7230 vik_layer_emit_update( VIK_LAYER(vtl) );
7235 gboolean has_same_waypoint_name;
7236 const gchar *same_waypoint_name;
7237 } same_waypoint_name_udata;
7239 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
7241 const gchar* namea = (const gchar*) aa;
7242 const gchar* nameb = (const gchar*) bb;
7245 gint result = strcmp ( namea, nameb );
7247 if ( result == 0 ) {
7248 // Found two names the same
7249 same_waypoint_name_udata *user_data = udata;
7250 user_data->has_same_waypoint_name = TRUE;
7251 user_data->same_waypoint_name = namea;
7254 // Leave ordering the same
7259 * Find out if any waypoints have the same name in this layer
7261 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
7263 // Sort items by name, then compare if any next to each other are the same
7265 GList *waypoint_names = NULL;
7266 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7269 if ( ! waypoint_names )
7272 same_waypoint_name_udata udata;
7273 udata.has_same_waypoint_name = FALSE;
7275 // Use sort routine to traverse list comparing items
7276 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7277 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7278 // Still no waypoints...
7282 return udata.has_same_waypoint_name;
7286 * Force unqiue waypoint names for this layer
7287 * Note the panel is a required parameter to enable the update of the names displayed
7289 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7291 // . Search list for an instance of repeated name
7292 // . get waypoint of this name
7293 // . create new name
7294 // . rename waypoint & update equiv. treeview iter
7295 // . repeat until all different
7297 same_waypoint_name_udata udata;
7299 GList *waypoint_names = NULL;
7300 udata.has_same_waypoint_name = FALSE;
7301 udata.same_waypoint_name = NULL;
7303 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7306 if ( ! waypoint_names )
7309 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7311 // Still no waypoints...
7312 if ( ! dummy_list1 )
7315 while ( udata.has_same_waypoint_name ) {
7317 // Find a waypoint with the same name
7318 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7322 g_critical("Houston, we've had a problem.");
7323 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7324 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7329 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7331 trw_layer_waypoint_rename ( vtl, waypoint, newname );
7333 // Start trying to find same names again...
7334 waypoint_names = NULL;
7335 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7336 udata.has_same_waypoint_name = FALSE;
7337 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7339 // No waypoints any more - give up searching
7340 if ( ! dummy_list2 )
7341 udata.has_same_waypoint_name = FALSE;
7345 vik_layers_panel_emit_update ( vlp );
7351 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7353 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7356 // Ensure list of waypoint names offered is unique
7357 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7358 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7359 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7360 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7366 // Sort list alphabetically for better presentation
7367 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7369 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7373 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7375 // Get list of items to delete from the user
7376 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7379 _("Delete Selection"),
7380 _("Select waypoints to delete"));
7383 // Delete requested waypoints
7384 // since specificly requested, IMHO no need for extra confirmation
7385 if ( delete_list ) {
7387 for (l = delete_list; l != NULL; l = g_list_next(l)) {
7388 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7389 trw_layer_delete_waypoint_by_name (vtl, l->data);
7391 g_list_free(delete_list);
7393 trw_layer_calculate_bounds_waypoints ( vtl );
7394 // Reset layer timestamp in case it has now changed
7395 vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7396 vik_layer_emit_update( VIK_LAYER(vtl) );
7404 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7406 vik_treeview_item_toggle_visible ( vt, it );
7412 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7414 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7420 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7422 wp->visible = GPOINTER_TO_INT (on_off);
7428 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7430 wp->visible = !wp->visible;
7436 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7438 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7439 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7440 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7441 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7443 vik_layer_emit_update ( VIK_LAYER(vtl) );
7449 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7451 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7452 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7453 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7454 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7456 vik_layer_emit_update ( VIK_LAYER(vtl) );
7462 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7464 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7465 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7466 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7468 vik_layer_emit_update ( VIK_LAYER(vtl) );
7474 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7476 trk->visible = GPOINTER_TO_INT (on_off);
7482 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7484 trk->visible = !trk->visible;
7490 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7492 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7493 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7494 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7495 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7497 vik_layer_emit_update ( VIK_LAYER(vtl) );
7503 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7505 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7506 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7507 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7508 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7510 vik_layer_emit_update ( VIK_LAYER(vtl) );
7516 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7518 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7519 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7520 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7522 vik_layer_emit_update ( VIK_LAYER(vtl) );
7528 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7530 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7531 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7532 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7533 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7535 vik_layer_emit_update ( VIK_LAYER(vtl) );
7541 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7543 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7544 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7545 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7546 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7548 vik_layer_emit_update ( VIK_LAYER(vtl) );
7554 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7556 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7557 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7558 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7560 vik_layer_emit_update ( VIK_LAYER(vtl) );
7564 * vik_trw_layer_build_waypoint_list_t:
7566 * Helper function to construct a list of #vik_trw_waypoint_list_t
7568 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7570 GList *waypoints_and_layers = NULL;
7571 // build waypoints_and_layers list
7572 while ( waypoints ) {
7573 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7574 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7576 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7577 waypoints = g_list_next ( waypoints );
7579 return waypoints_and_layers;
7583 * trw_layer_create_waypoint_list:
7585 * Create the latest list of waypoints with the associated layer(s)
7586 * Although this will always be from a single layer here
7588 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7590 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7591 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7593 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7597 * trw_layer_analyse_close:
7599 * Stuff to do on dialog closure
7601 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7603 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7604 gtk_widget_destroy ( dialog );
7605 vtl->tracks_analysis_dialog = NULL;
7609 * vik_trw_layer_build_track_list_t:
7611 * Helper function to construct a list of #vik_trw_track_list_t
7613 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7615 GList *tracks_and_layers = NULL;
7616 // build tracks_and_layers list
7618 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7619 vtdl->trk = VIK_TRACK(tracks->data);
7621 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7622 tracks = g_list_next ( tracks );
7624 return tracks_and_layers;
7628 * trw_layer_create_track_list:
7630 * Create the latest list of tracks with the associated layer(s)
7631 * Although this will always be from a single layer here
7633 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7635 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7636 GList *tracks = NULL;
7637 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7638 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7640 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7642 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7645 static void trw_layer_tracks_stats ( menu_array_layer values )
7647 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7648 // There can only be one!
7649 if ( vtl->tracks_analysis_dialog )
7652 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7653 VIK_LAYER(vtl)->name,
7655 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7656 trw_layer_create_track_list,
7657 trw_layer_analyse_close );
7663 static void trw_layer_routes_stats ( menu_array_layer values )
7665 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7666 // There can only be one!
7667 if ( vtl->tracks_analysis_dialog )
7670 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7671 VIK_LAYER(vtl)->name,
7673 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7674 trw_layer_create_track_list,
7675 trw_layer_analyse_close );
7678 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7680 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7681 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7683 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7686 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7688 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7689 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7692 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7693 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7697 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7699 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7700 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7704 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7705 } else if ( !strncmp(wp->comment, "http", 4) ) {
7706 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7707 } else if ( !strncmp(wp->description, "http", 4) ) {
7708 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7712 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7714 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7716 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7718 // No actual change to the name supplied
7720 if (strcmp(newname, wp->name) == 0 )
7723 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7726 // An existing waypoint has been found with the requested name
7727 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7728 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7733 // Update WP name and refresh the treeview
7734 vik_waypoint_set_name (wp, newname);
7736 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7737 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7739 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7744 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7746 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7748 // No actual change to the name supplied
7750 if (strcmp(newname, trk->name) == 0)
7753 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7756 // An existing track has been found with the requested name
7757 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7758 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7762 // Update track name and refresh GUI parts
7763 vik_track_set_name (trk, newname);
7765 // Update any subwindows that could be displaying this track which has changed name
7766 // Only one Track Edit Window
7767 if ( l->current_tp_track == trk && l->tpwin ) {
7768 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7770 // Property Dialog of the track
7771 vik_trw_layer_propwin_update ( trk );
7773 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7774 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7776 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7781 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7783 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7785 // No actual change to the name supplied
7787 if (strcmp(newname, trk->name) == 0)
7790 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7793 // An existing track has been found with the requested name
7794 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7795 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7799 // Update track name and refresh GUI parts
7800 vik_track_set_name (trk, newname);
7802 // Update any subwindows that could be displaying this track which has changed name
7803 // Only one Track Edit Window
7804 if ( l->current_tp_track == trk && l->tpwin ) {
7805 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7807 // Property Dialog of the track
7808 vik_trw_layer_propwin_update ( trk );
7810 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7811 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7813 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7820 static gboolean is_valid_geocache_name ( gchar *str )
7822 gint len = strlen ( str );
7823 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]));
7827 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7829 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7830 a_acquire_set_filter_track ( trk );
7834 #ifdef VIK_CONFIG_GOOGLE
7835 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7837 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7838 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7841 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7843 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7845 gchar *escaped = uri_escape ( tr->comment );
7846 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7847 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7854 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7855 /* viewpoint is now available instead */
7856 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7858 static menu_array_sublayer pass_along;
7860 gboolean rv = FALSE;
7862 pass_along[MA_VTL] = l;
7863 pass_along[MA_VLP] = vlp;
7864 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7865 pass_along[MA_SUBLAYER_ID] = sublayer;
7866 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7867 pass_along[MA_VVP] = vvp;
7868 pass_along[MA_TV_ITER] = iter;
7869 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
7871 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7875 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7877 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7878 gtk_widget_show ( item );
7880 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7881 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7882 if (tr && tr->property_dialog)
7883 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7885 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7886 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7887 if (tr && tr->property_dialog)
7888 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7891 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7892 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7893 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7894 gtk_widget_show ( item );
7896 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7897 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7898 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7899 gtk_widget_show ( item );
7901 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7902 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7903 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7904 gtk_widget_show ( item );
7906 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7908 // Always create separator as now there is always at least the transform menu option
7909 item = gtk_menu_item_new ();
7910 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7911 gtk_widget_show ( item );
7913 /* could be a right-click using the tool */
7914 if ( vlp != NULL ) {
7915 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7916 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7917 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7918 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7919 gtk_widget_show ( item );
7922 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7924 if ( wp && wp->name ) {
7925 if ( is_valid_geocache_name ( wp->name ) ) {
7926 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7929 gtk_widget_show ( item );
7931 #ifdef VIK_CONFIG_GEOTAG
7932 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7933 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7934 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7935 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7936 gtk_widget_show ( item );
7940 if ( wp && wp->image )
7942 // Set up image paramater
7943 pass_along[MA_MISC] = wp->image;
7945 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7946 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
7947 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7948 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7949 gtk_widget_show ( item );
7951 #ifdef VIK_CONFIG_GEOTAG
7952 GtkWidget *geotag_submenu = gtk_menu_new ();
7953 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7954 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7955 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7956 gtk_widget_show ( item );
7957 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7959 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7960 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7961 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7962 gtk_widget_show ( item );
7964 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7966 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7967 gtk_widget_show ( item );
7974 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7975 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7976 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7977 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7978 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7979 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7980 gtk_widget_show ( item );
7986 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7987 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7989 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7990 gtk_widget_show ( item );
7991 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7992 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7993 gtk_widget_set_sensitive ( item, TRUE );
7995 gtk_widget_set_sensitive ( item, FALSE );
7998 item = gtk_menu_item_new ();
7999 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8000 gtk_widget_show ( item );
8003 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
8006 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
8007 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8008 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
8009 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8010 gtk_widget_show ( item );
8013 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
8015 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
8016 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8017 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
8018 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8019 gtk_widget_show ( item );
8021 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
8022 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
8024 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8025 gtk_widget_show ( item );
8027 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
8028 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
8030 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8031 gtk_widget_show ( item );
8033 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
8034 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
8036 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8037 gtk_widget_show ( item );
8039 GtkWidget *vis_submenu = gtk_menu_new ();
8040 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8041 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8042 gtk_widget_show ( item );
8043 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8045 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
8046 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
8048 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8049 gtk_widget_show ( item );
8051 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
8052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
8054 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8055 gtk_widget_show ( item );
8057 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8058 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8059 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
8060 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8061 gtk_widget_show ( item );
8063 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
8064 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8065 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
8066 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8069 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
8073 if ( l->current_track && !l->current_track->is_route ) {
8074 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8076 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8077 gtk_widget_show ( item );
8079 item = gtk_menu_item_new ();
8080 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8081 gtk_widget_show ( item );
8084 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
8085 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
8087 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8088 gtk_widget_show ( item );
8090 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
8091 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8092 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
8093 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8094 gtk_widget_show ( item );
8095 // Make it available only when a new track *not* already in progress
8096 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8098 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
8099 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8100 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
8101 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8102 gtk_widget_show ( item );
8104 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
8105 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
8107 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8108 gtk_widget_show ( item );
8110 GtkWidget *vis_submenu = gtk_menu_new ();
8111 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8112 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8113 gtk_widget_show ( item );
8114 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8116 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
8117 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
8119 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8120 gtk_widget_show ( item );
8122 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
8123 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8124 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
8125 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8126 gtk_widget_show ( item );
8128 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8129 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
8131 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8133 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
8134 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8136 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8137 gtk_widget_show ( item );
8139 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
8141 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8142 gtk_widget_show ( item );
8145 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
8149 if ( l->current_track && l->current_track->is_route ) {
8150 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8151 // Reuse finish track method
8152 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8153 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8154 gtk_widget_show ( item );
8156 item = gtk_menu_item_new ();
8157 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8158 gtk_widget_show ( item );
8161 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
8162 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8163 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
8164 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8165 gtk_widget_show ( item );
8167 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
8168 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8169 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
8170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8171 gtk_widget_show ( item );
8172 // Make it available only when a new track *not* already in progress
8173 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8175 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
8176 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8177 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
8178 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8179 gtk_widget_show ( item );
8181 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
8182 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8183 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
8184 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8185 gtk_widget_show ( item );
8187 GtkWidget *vis_submenu = gtk_menu_new ();
8188 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8189 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8190 gtk_widget_show ( item );
8191 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8193 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
8194 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8195 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
8196 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8197 gtk_widget_show ( item );
8199 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
8200 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8201 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
8202 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8203 gtk_widget_show ( item );
8205 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8206 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8207 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
8208 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8210 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
8211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8213 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8215 gtk_widget_show ( item );
8217 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
8219 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8220 gtk_widget_show ( item );
8224 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8225 GtkWidget *submenu_sort = gtk_menu_new ();
8226 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
8227 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8228 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8229 gtk_widget_show ( item );
8230 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
8232 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
8233 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8234 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
8235 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8236 gtk_widget_show ( item );
8238 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
8239 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8240 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
8241 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8242 gtk_widget_show ( item );
8244 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") );
8245 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8246 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along );
8247 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8248 gtk_widget_show ( item );
8250 item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") );
8251 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8252 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along );
8253 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8254 gtk_widget_show ( item );
8257 GtkWidget *upload_submenu = gtk_menu_new ();
8259 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8261 item = gtk_menu_item_new ();
8262 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8263 gtk_widget_show ( item );
8265 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
8266 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8267 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
8268 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8269 if ( l->current_track ) {
8270 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8271 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8272 gtk_widget_show ( item );
8275 item = gtk_menu_item_new ();
8276 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8277 gtk_widget_show ( item );
8280 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8281 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
8283 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
8284 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8285 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8286 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8287 gtk_widget_show ( item );
8289 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8292 gtk_widget_show ( item );
8294 GtkWidget *goto_submenu;
8295 goto_submenu = gtk_menu_new ();
8296 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8297 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8298 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8299 gtk_widget_show ( item );
8300 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8302 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8303 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8304 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8305 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8306 gtk_widget_show ( item );
8308 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8309 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8311 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8312 gtk_widget_show ( item );
8314 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8315 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8316 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8317 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8318 gtk_widget_show ( item );
8320 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8321 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8323 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8324 gtk_widget_show ( item );
8326 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8327 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8329 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8330 gtk_widget_show ( item );
8332 // Routes don't have speeds
8333 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8334 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8335 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8336 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8337 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8338 gtk_widget_show ( item );
8341 GtkWidget *combine_submenu;
8342 combine_submenu = gtk_menu_new ();
8343 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8344 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8345 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8346 gtk_widget_show ( item );
8347 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8349 // Routes don't have times or segments...
8350 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8351 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8352 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8353 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8354 gtk_widget_show ( item );
8356 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8357 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8358 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8359 gtk_widget_show ( item );
8362 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8363 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8364 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8365 gtk_widget_show ( item );
8367 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8368 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8370 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8372 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8373 gtk_widget_show ( item );
8375 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8376 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8378 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8379 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8380 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8381 gtk_widget_show ( item );
8383 GtkWidget *split_submenu;
8384 split_submenu = gtk_menu_new ();
8385 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8386 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8387 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8388 gtk_widget_show ( item );
8389 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8391 // Routes don't have times or segments...
8392 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8393 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8394 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8395 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8396 gtk_widget_show ( item );
8398 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8399 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8400 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8401 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8402 gtk_widget_show ( item );
8405 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8406 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8407 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8408 gtk_widget_show ( item );
8410 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8411 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8412 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8413 gtk_widget_show ( item );
8414 // Make it available only when a trackpoint is selected.
8415 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8417 GtkWidget *insert_submenu = gtk_menu_new ();
8418 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8419 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8420 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8421 gtk_widget_show ( item );
8422 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8424 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8425 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8426 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8427 gtk_widget_show ( item );
8428 // Make it available only when a point is selected
8429 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8431 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8432 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8433 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8434 gtk_widget_show ( item );
8435 // Make it available only when a point is selected
8436 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8438 GtkWidget *delete_submenu;
8439 delete_submenu = gtk_menu_new ();
8440 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8441 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8442 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8443 gtk_widget_show ( item );
8444 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8446 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8447 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8448 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8449 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8450 gtk_widget_show ( item );
8451 // Make it available only when a point is selected
8452 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8454 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8455 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8456 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8457 gtk_widget_show ( item );
8459 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8460 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8461 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8462 gtk_widget_show ( item );
8464 GtkWidget *transform_submenu;
8465 transform_submenu = gtk_menu_new ();
8466 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8467 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8468 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8469 gtk_widget_show ( item );
8470 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8472 GtkWidget *dem_submenu;
8473 dem_submenu = gtk_menu_new ();
8474 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8475 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
8476 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8477 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8479 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8480 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8481 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8482 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8483 gtk_widget_show ( item );
8485 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8486 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8487 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8488 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8489 gtk_widget_show ( item );
8491 GtkWidget *smooth_submenu;
8492 smooth_submenu = gtk_menu_new ();
8493 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8494 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8495 gtk_widget_show ( item );
8496 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8498 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8499 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8500 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8501 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8502 gtk_widget_show ( item );
8504 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8505 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8506 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8507 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8508 gtk_widget_show ( item );
8510 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8511 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8513 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8514 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8515 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8516 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8517 gtk_widget_show ( item );
8519 // Routes don't have timestamps - so these are only available for tracks
8520 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8521 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8522 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8523 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8524 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8525 gtk_widget_show ( item );
8527 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") );
8528 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along );
8529 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8530 gtk_widget_set_tooltip_text (item, _("Reset trackpoint timestamps between the first and last points such that track is traveled at equal speed"));
8531 gtk_widget_show ( item );
8534 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8535 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8537 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8538 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8539 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8540 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8541 gtk_widget_show ( item );
8543 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8544 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8545 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8546 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8547 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8548 gtk_widget_show ( item );
8551 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8553 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8554 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8556 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8557 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
8558 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8559 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8560 gtk_widget_show ( item );
8563 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8564 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8566 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8567 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8568 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8569 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8570 gtk_widget_show ( item );
8572 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8573 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8575 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8576 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8577 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8578 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8579 gtk_widget_show ( item );
8581 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8582 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8583 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
8584 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8585 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8586 gtk_widget_show ( item );
8589 // ATM can't upload a single waypoint but can do waypoints to a GPS
8590 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8591 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8592 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8593 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8594 gtk_widget_show ( item );
8595 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8597 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8598 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8599 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8600 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8601 gtk_widget_show ( item );
8605 GtkWidget *external_submenu = create_external_submenu ( menu );
8607 // These are only made available if a suitable program is installed
8608 if ( (have_astro_program || have_diary_program) &&
8609 (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
8611 if ( have_diary_program ) {
8612 item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
8613 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8614 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8615 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8616 gtk_widget_set_tooltip_text (item, _("Open diary program at this date"));
8617 gtk_widget_show ( item );
8620 if ( have_astro_program ) {
8621 item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
8622 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
8623 gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8624 gtk_widget_set_tooltip_text (item, _("Open astronomy program at this date and location"));
8625 gtk_widget_show ( item );
8629 if ( l->current_tpl || l->current_wp ) {
8630 // For the selected point
8632 if ( l->current_tpl )
8633 vc = &(VIK_TRACKPOINT(l->current_tpl->data)->coord);
8635 vc = &(l->current_wp->coord);
8636 vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), vc );
8639 // Otherwise for the selected sublayer
8640 // TODO: Should use selected items centre - rather than implicitly using the current viewport
8641 vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), NULL );
8645 #ifdef VIK_CONFIG_GOOGLE
8646 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8648 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8651 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8652 gtk_widget_show ( item );
8656 // Some things aren't usable with routes
8657 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8658 #ifdef VIK_CONFIG_OPENSTREETMAP
8659 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8660 // Convert internal pointer into track
8661 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8662 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8663 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8664 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8665 gtk_widget_show ( item );
8668 // Currently filter with functions all use shellcommands and thus don't work in Windows
8670 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8671 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8672 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8673 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8674 gtk_widget_show ( item );
8677 /* ATM This function is only available via the layers panel, due to needing a vlp */
8679 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8680 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8681 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8683 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8684 gtk_widget_show ( item );
8688 #ifdef VIK_CONFIG_GEOTAG
8689 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8690 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8691 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8692 gtk_widget_show ( item );
8696 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8697 // Only show on viewport popmenu when a trackpoint is selected
8698 if ( ! vlp && l->current_tpl ) {
8700 item = gtk_menu_item_new ();
8701 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8702 gtk_widget_show ( item );
8704 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8705 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8706 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8707 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8708 gtk_widget_show ( item );
8712 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8713 GtkWidget *transform_submenu;
8714 transform_submenu = gtk_menu_new ();
8715 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8716 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8717 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8718 gtk_widget_show ( item );
8719 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8721 GtkWidget *dem_submenu;
8722 dem_submenu = gtk_menu_new ();
8723 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8724 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
8725 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8726 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8728 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8729 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8730 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8731 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8732 gtk_widget_show ( item );
8734 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8735 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8736 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8737 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8738 gtk_widget_show ( item );
8741 gtk_widget_show_all ( GTK_WIDGET(menu) );
8746 // TODO: Probably better to rework this track manipulation in viktrack.c
8747 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8750 if (!vtl->current_tpl)
8753 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8754 VikTrackpoint *tp_other = NULL;
8757 if (!vtl->current_tpl->prev)
8759 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8761 if (!vtl->current_tpl->next)
8763 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8766 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8769 VikTrackpoint *tp_new = vik_trackpoint_new();
8770 struct LatLon ll_current, ll_other;
8771 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8772 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8774 /* main positional interpolation */
8775 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8776 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8778 /* Now other properties that can be interpolated */
8779 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8781 if (tp_current->has_timestamp && tp_other->has_timestamp) {
8782 /* Note here the division is applied to each part, then added
8783 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8784 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8785 tp_new->has_timestamp = TRUE;
8788 if (tp_current->speed != NAN && tp_other->speed != NAN)
8789 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8791 /* TODO - improve interpolation of course, as it may not be correct.
8792 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8793 [similar applies if value is in radians] */
8794 if (tp_current->course != NAN && tp_other->course != NAN)
8795 tp_new->course = (tp_current->course + tp_other->course)/2;
8797 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8799 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8800 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8802 // Otherwise try routes
8803 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8807 gint index = g_list_index ( trk->trackpoints, tp_current );
8811 // NB no recalculation of bounds since it is inserted between points
8812 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8817 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8823 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8827 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8829 if ( vtl->current_tpl )
8831 vtl->current_tpl = NULL;
8832 vtl->current_tp_track = NULL;
8833 vtl->current_tp_id = NULL;
8834 vik_layer_emit_update(VIK_LAYER(vtl));
8838 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8840 VikTrack *trk = vtl->current_tp_track;
8842 // Notional center of a track is simply an average of the bounding box extremities
8843 struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8844 vik_coord_load_from_latlon ( &vc, vtl->coord_mode, ¢er );
8845 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8848 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8850 g_assert ( vtl->tpwin != NULL );
8851 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8852 trw_layer_cancel_current_tp ( vtl, TRUE );
8854 if ( vtl->current_tpl == NULL )
8857 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8859 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8860 my_tpwin_set_tp ( vtl );
8862 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8864 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8866 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8870 trw_layer_trackpoint_selected_delete ( vtl, tr );
8872 if ( vtl->current_tpl )
8873 // Reset dialog with the available adjacent trackpoint
8874 my_tpwin_set_tp ( vtl );
8876 vik_layer_emit_update(VIK_LAYER(vtl));
8878 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8880 if ( vtl->current_tp_track ) {
8881 vtl->current_tpl = vtl->current_tpl->next;
8882 my_tpwin_set_tp ( vtl );
8884 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8886 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8888 if ( vtl->current_tp_track ) {
8889 vtl->current_tpl = vtl->current_tpl->prev;
8890 my_tpwin_set_tp ( vtl );
8892 vik_layer_emit_update(VIK_LAYER(vtl));
8894 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8896 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8897 vik_layer_emit_update(VIK_LAYER(vtl));
8899 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8900 vik_layer_emit_update(VIK_LAYER(vtl));
8904 * trw_layer_dialog_shift:
8905 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8907 * Try to reposition a dialog if it's over the specified coord
8908 * so to not obscure the item of interest
8910 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8912 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8914 // Attempt force dialog to be shown so we can find out where it is more reliably...
8915 while ( gtk_events_pending() )
8916 gtk_main_iteration ();
8918 // get parent window position & size
8919 gint win_pos_x, win_pos_y;
8920 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8922 gint win_size_x, win_size_y;
8923 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8925 // get own dialog size
8926 gint dia_size_x, dia_size_y;
8927 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8929 // get own dialog position
8930 gint dia_pos_x, dia_pos_y;
8931 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8933 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8934 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8936 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8938 gint vp_xx, vp_yy; // In viewport pixels
8939 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8941 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8945 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8947 // Transform Viewport pixels into absolute pixels
8948 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8949 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8951 // Is dialog over the point (to within an ^^ edge value)
8952 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8953 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8957 gint hh = vik_viewport_get_height ( vvp );
8959 // Consider the difference in viewport to the full window
8960 gint offset_y = dest_y;
8961 // Add difference between dialog and window sizes
8962 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8964 if ( vp_yy > hh/2 ) {
8965 // Point in bottom half, move window to top half
8966 gtk_window_move ( dialog, dia_pos_x, offset_y );
8969 // Point in top half, move dialog down
8970 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8974 // Shift left<->right
8975 gint ww = vik_viewport_get_width ( vvp );
8977 // Consider the difference in viewport to the full window
8978 gint offset_x = dest_x;
8979 // Add difference between dialog and window sizes
8980 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8982 if ( vp_xx > ww/2 ) {
8983 // Point on right, move window to left
8984 gtk_window_move ( dialog, offset_x, dia_pos_y );
8987 // Point on left, move right
8988 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8996 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
9000 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9001 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
9002 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
9003 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
9005 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
9007 if ( vtl->current_tpl ) {
9008 // get tp pixel position
9009 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9011 // Shift up<->down to try not to obscure the trackpoint.
9012 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
9016 if ( vtl->current_tpl )
9017 if ( vtl->current_tp_track )
9018 my_tpwin_set_tp ( vtl );
9019 /* set layer name and TP data */
9022 /***************************************************************************
9024 ***************************************************************************/
9026 /*** Utility data structures and functions ****/
9030 gint closest_x, closest_y;
9031 gboolean draw_images;
9032 gpointer *closest_wp_id;
9033 VikWaypoint *closest_wp;
9039 gint closest_x, closest_y;
9040 gpointer closest_track_id;
9041 VikTrackpoint *closest_tp;
9047 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
9053 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
9055 // If waypoint has an image then use the image size to select
9056 if ( params->draw_images && wp->image ) {
9057 gint slackx, slacky;
9058 slackx = wp->image_width / 2;
9059 slacky = wp->image_height / 2;
9061 if ( x <= params->x + slackx && x >= params->x - slackx
9062 && y <= params->y + slacky && y >= params->y - slacky ) {
9063 params->closest_wp_id = id;
9064 params->closest_wp = wp;
9065 params->closest_x = x;
9066 params->closest_y = y;
9069 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
9070 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
9071 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9073 params->closest_wp_id = id;
9074 params->closest_wp = wp;
9075 params->closest_x = x;
9076 params->closest_y = y;
9080 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
9082 GList *tpl = t->trackpoints;
9088 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
9094 tp = VIK_TRACKPOINT(tpl->data);
9096 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
9098 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
9099 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
9100 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9102 params->closest_track_id = id;
9103 params->closest_tp = tp;
9104 params->closest_tpl = tpl;
9105 params->closest_x = x;
9106 params->closest_y = y;
9112 // ATM: Leave this as 'Track' only.
9113 // Not overly bothered about having a snap to route trackpoint capability
9114 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9116 TPSearchParams params;
9120 params.closest_track_id = NULL;
9121 params.closest_tp = NULL;
9122 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9123 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
9124 return params.closest_tp;
9127 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9129 WPSearchParams params;
9133 params.draw_images = vtl->drawimages;
9134 params.closest_wp = NULL;
9135 params.closest_wp_id = NULL;
9136 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9137 return params.closest_wp;
9141 // Some forward declarations
9142 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
9143 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
9144 static void marker_end_move ( tool_ed_t *t );
9147 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
9151 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9153 // Here always allow snapping back to the original location
9154 // this is useful when one decides not to move the thing afterall
9155 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
9158 if ( event->state & GDK_CONTROL_MASK )
9160 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9162 new_coord = tp->coord;
9166 if ( event->state & GDK_SHIFT_MASK )
9168 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9170 new_coord = wp->coord;
9174 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9176 marker_moveto ( t, x, y );
9183 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
9185 if ( t->holding && event->button == 1 )
9187 // Prevent accidental (small) shifts when specific movement has not been requested
9188 // (as the click release has occurred within the click object detection area)
9193 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9196 if ( event->state & GDK_CONTROL_MASK )
9198 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9200 new_coord = tp->coord;
9204 if ( event->state & GDK_SHIFT_MASK )
9206 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9208 new_coord = wp->coord;
9211 marker_end_move ( t );
9213 // Determine if working on a waypoint or a trackpoint
9214 if ( t->is_waypoint ) {
9215 // Update waypoint position
9216 vtl->current_wp->coord = new_coord;
9217 trw_layer_calculate_bounds_waypoints ( vtl );
9218 // Reset waypoint pointer
9219 vtl->current_wp = NULL;
9220 vtl->current_wp_id = NULL;
9223 if ( vtl->current_tpl ) {
9224 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9226 if ( vtl->current_tp_track )
9227 vik_track_calculate_bounds ( vtl->current_tp_track );
9230 if ( vtl->current_tp_track )
9231 my_tpwin_set_tp ( vtl );
9232 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
9236 vik_layer_emit_update ( VIK_LAYER(vtl) );
9243 Returns true if a waypoint or track is found near the requested event position for this particular layer
9244 The item found is automatically selected
9245 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
9247 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
9249 if ( event->button != 1 )
9252 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9255 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9259 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
9261 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
9263 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
9264 WPSearchParams wp_params;
9265 wp_params.vvp = vvp;
9266 wp_params.x = event->x;
9267 wp_params.y = event->y;
9268 wp_params.draw_images = vtl->drawimages;
9269 wp_params.closest_wp_id = NULL;
9270 wp_params.closest_wp = NULL;
9272 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
9274 if ( wp_params.closest_wp ) {
9277 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
9279 // Too easy to move it so must be holding shift to start immediately moving it
9280 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
9281 if ( event->state & GDK_SHIFT_MASK ||
9282 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
9283 // Put into 'move buffer'
9284 // NB vvp & vw already set in tet
9285 tet->vtl = (gpointer)vtl;
9286 tet->is_waypoint = TRUE;
9288 marker_begin_move (tet, event->x, event->y);
9291 vtl->current_wp = wp_params.closest_wp;
9292 vtl->current_wp_id = wp_params.closest_wp_id;
9294 if ( event->type == GDK_2BUTTON_PRESS ) {
9295 if ( vtl->current_wp->image ) {
9296 menu_array_sublayer values;
9297 values[MA_VTL] = vtl;
9298 values[MA_MISC] = vtl->current_wp->image;
9299 trw_layer_show_picture ( values );
9303 vik_layer_emit_update ( VIK_LAYER(vtl) );
9309 // Used for both track and route lists
9310 TPSearchParams tp_params;
9311 tp_params.vvp = vvp;
9312 tp_params.x = event->x;
9313 tp_params.y = event->y;
9314 tp_params.closest_track_id = NULL;
9315 tp_params.closest_tp = NULL;
9316 tp_params.closest_tpl = NULL;
9317 tp_params.bbox = bbox;
9319 if (vtl->tracks_visible) {
9320 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9322 if ( tp_params.closest_tp ) {
9324 // Always select + highlight the track
9325 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9327 tet->is_waypoint = FALSE;
9329 // Select the Trackpoint
9330 // Can move it immediately when control held or it's the previously selected tp
9331 if ( event->state & GDK_CONTROL_MASK ||
9332 vtl->current_tpl == tp_params.closest_tpl ) {
9333 // Put into 'move buffer'
9334 // NB vvp & vw already set in tet
9335 tet->vtl = (gpointer)vtl;
9336 marker_begin_move (tet, event->x, event->y);
9339 vtl->current_tpl = tp_params.closest_tpl;
9340 vtl->current_tp_id = tp_params.closest_track_id;
9341 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9343 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9346 my_tpwin_set_tp ( vtl );
9348 vik_layer_emit_update ( VIK_LAYER(vtl) );
9353 // Try again for routes
9354 if (vtl->routes_visible) {
9355 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9357 if ( tp_params.closest_tp ) {
9359 // Always select + highlight the track
9360 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9362 tet->is_waypoint = FALSE;
9364 // Select the Trackpoint
9365 // Can move it immediately when control held or it's the previously selected tp
9366 if ( event->state & GDK_CONTROL_MASK ||
9367 vtl->current_tpl == tp_params.closest_tpl ) {
9368 // Put into 'move buffer'
9369 // NB vvp & vw already set in tet
9370 tet->vtl = (gpointer)vtl;
9371 marker_begin_move (tet, event->x, event->y);
9374 vtl->current_tpl = tp_params.closest_tpl;
9375 vtl->current_tp_id = tp_params.closest_track_id;
9376 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9378 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9381 my_tpwin_set_tp ( vtl );
9383 vik_layer_emit_update ( VIK_LAYER(vtl) );
9388 /* these aren't the droids you're looking for */
9389 vtl->current_wp = NULL;
9390 vtl->current_wp_id = NULL;
9391 trw_layer_cancel_current_tp ( vtl, FALSE );
9394 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9399 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9401 if ( event->button != 3 )
9404 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9407 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9410 /* Post menu for the currently selected item */
9412 /* See if a track is selected */
9413 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9414 if ( track && track->visible ) {
9416 if ( track->name ) {
9418 if ( vtl->track_right_click_menu )
9419 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9421 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9428 if ( track->is_route )
9429 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9431 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9433 if ( trkf && udataU.uuid ) {
9436 if ( track->is_route )
9437 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9439 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9441 trw_layer_sublayer_add_menu_items ( vtl,
9442 vtl->track_right_click_menu,
9444 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9450 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9456 /* See if a waypoint is selected */
9457 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9458 if ( waypoint && waypoint->visible ) {
9459 if ( waypoint->name ) {
9461 if ( vtl->wp_right_click_menu )
9462 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9464 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9467 udata.wp = waypoint;
9470 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9472 if ( wpf && udata.uuid ) {
9473 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9475 trw_layer_sublayer_add_menu_items ( vtl,
9476 vtl->wp_right_click_menu,
9478 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9483 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9492 /* background drawing hook, to be passed the viewport */
9493 static gboolean tool_sync_done = TRUE;
9495 static gboolean tool_sync(gpointer data)
9497 VikViewport *vvp = data;
9498 gdk_threads_enter();
9499 vik_viewport_sync(vvp);
9500 tool_sync_done = TRUE;
9501 gdk_threads_leave();
9505 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9508 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9509 gdk_gc_set_function ( t->gc, GDK_INVERT );
9510 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9511 vik_viewport_sync(t->vvp);
9517 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9519 VikViewport *vvp = t->vvp;
9520 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9521 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9526 if (tool_sync_done) {
9527 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9528 tool_sync_done = FALSE;
9532 static void marker_end_move ( tool_ed_t *t )
9534 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9535 g_object_unref ( t->gc );
9540 /*** Edit waypoint ****/
9542 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9544 tool_ed_t *t = g_new(tool_ed_t, 1);
9550 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9555 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9557 WPSearchParams params;
9558 tool_ed_t *t = data;
9559 VikViewport *vvp = t->vvp;
9561 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9568 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9571 if ( vtl->current_wp && vtl->current_wp->visible )
9573 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9575 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9577 if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX &&
9578 abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX )
9580 if ( event->button == 3 )
9581 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9583 marker_begin_move(t, event->x, event->y);
9590 params.x = event->x;
9591 params.y = event->y;
9592 params.draw_images = vtl->drawimages;
9593 params.closest_wp_id = NULL;
9594 params.closest_wp = NULL;
9595 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms);
9596 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9598 if ( event->button == 3 )
9599 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9601 marker_begin_move(t, event->x, event->y);
9604 else if ( params.closest_wp )
9606 if ( event->button == 3 )
9607 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9609 vtl->waypoint_rightclick = FALSE;
9611 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9613 vtl->current_wp = params.closest_wp;
9614 vtl->current_wp_id = params.closest_wp_id;
9616 /* could make it so don't update if old WP is off screen and new is null but oh well */
9617 vik_layer_emit_update ( VIK_LAYER(vtl) );
9621 vtl->current_wp = NULL;
9622 vtl->current_wp_id = NULL;
9623 vtl->waypoint_rightclick = FALSE;
9624 vik_layer_emit_update ( VIK_LAYER(vtl) );
9628 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9630 tool_ed_t *t = data;
9631 VikViewport *vvp = t->vvp;
9633 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9638 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9641 if ( event->state & GDK_CONTROL_MASK )
9643 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9645 new_coord = tp->coord;
9649 if ( event->state & GDK_SHIFT_MASK )
9651 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9652 if ( wp && wp != vtl->current_wp )
9653 new_coord = wp->coord;
9658 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9660 marker_moveto ( t, x, y );
9667 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9669 tool_ed_t *t = data;
9670 VikViewport *vvp = t->vvp;
9672 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9675 if ( t->holding && event->button == 1 )
9678 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9681 if ( event->state & GDK_CONTROL_MASK )
9683 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9685 new_coord = tp->coord;
9689 if ( event->state & GDK_SHIFT_MASK )
9691 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9692 if ( wp && wp != vtl->current_wp )
9693 new_coord = wp->coord;
9696 marker_end_move ( t );
9698 vtl->current_wp->coord = new_coord;
9700 trw_layer_calculate_bounds_waypoints ( vtl );
9701 vik_layer_emit_update ( VIK_LAYER(vtl) );
9704 /* PUT IN RIGHT PLACE!!! */
9705 if ( event->button == 3 && vtl->waypoint_rightclick )
9707 if ( vtl->wp_right_click_menu )
9708 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9709 if ( vtl->current_wp ) {
9710 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9711 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 );
9712 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9714 vtl->waypoint_rightclick = FALSE;
9719 /*** New track ****/
9721 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9728 GdkDrawable *drawable;
9734 * Draw specified pixmap
9736 static gboolean draw_sync ( gpointer data )
9738 draw_sync_t *ds = (draw_sync_t*) data;
9739 // Sometimes don't want to draw
9740 // normally because another update has taken precedent such as panning the display
9741 // which means this pixmap is no longer valid
9742 if ( ds->vtl->draw_sync_do ) {
9743 gdk_threads_enter();
9744 gdk_draw_drawable (ds->drawable,
9747 0, 0, 0, 0, -1, -1);
9748 ds->vtl->draw_sync_done = TRUE;
9749 gdk_threads_leave();
9755 static gchar* distance_string (gdouble distance)
9759 /* draw label with distance */
9760 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9761 switch (dist_units) {
9762 case VIK_UNITS_DISTANCE_MILES:
9763 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9764 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9765 } else if (distance < 1609.4) {
9766 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9768 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9771 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9772 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9773 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9774 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9775 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9777 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9781 // VIK_UNITS_DISTANCE_KILOMETRES
9782 if (distance >= 1000 && distance < 100000) {
9783 g_sprintf(str, "%3.2f km", distance/1000.0);
9784 } else if (distance < 1000) {
9785 g_sprintf(str, "%d m", (int)distance);
9787 g_sprintf(str, "%d km", (int)distance/1000);
9791 return g_strdup (str);
9795 * Actually set the message in statusbar
9797 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9799 // Only show elevation data when track has some elevation properties
9800 gchar str_gain_loss[64];
9801 str_gain_loss[0] = '\0';
9802 gchar str_last_step[64];
9803 str_last_step[0] = '\0';
9804 gchar *str_total = distance_string (distance);
9806 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9807 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9808 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9810 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9813 if ( last_step > 0 ) {
9814 gchar *tmp = distance_string (last_step);
9815 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9819 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9821 // Write with full gain/loss information
9822 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9823 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9825 g_free ( str_total );
9829 * Figure out what information should be set in the statusbar and then write it
9831 static void update_statusbar ( VikTrwLayer *vtl )
9833 // Get elevation data
9834 gdouble elev_gain, elev_loss;
9835 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9837 /* Find out actual distance of current track */
9838 gdouble distance = vik_track_get_length (vtl->current_track);
9840 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9844 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9846 /* if we haven't sync'ed yet, we don't have time to do more. */
9847 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9848 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9850 static GdkPixmap *pixmap = NULL;
9852 // Need to check in case window has been resized
9853 w1 = vik_viewport_get_width(vvp);
9854 h1 = vik_viewport_get_height(vvp);
9856 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9858 gdk_drawable_get_size (pixmap, &w2, &h2);
9859 if (w1 != w2 || h1 != h2) {
9860 g_object_unref ( G_OBJECT ( pixmap ) );
9861 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9864 // Reset to background
9865 gdk_draw_drawable (pixmap,
9866 vtl->current_track_newpoint_gc,
9867 vik_viewport_get_pixmap(vvp),
9868 0, 0, 0, 0, -1, -1);
9870 draw_sync_t *passalong;
9873 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9875 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9876 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9877 // thus when we come to reset to the background it would include what we have already drawn!!
9878 gdk_draw_line ( pixmap,
9879 vtl->current_track_newpoint_gc,
9880 x1, y1, event->x, event->y );
9881 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9883 /* Find out actual distance of current track */
9884 gdouble distance = vik_track_get_length (vtl->current_track);
9886 // Now add distance to where the pointer is //
9889 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9890 vik_coord_to_latlon ( &coord, &ll );
9891 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9892 distance = distance + last_step;
9894 // Get elevation data
9895 gdouble elev_gain, elev_loss;
9896 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9898 // Adjust elevation data (if available) for the current pointer position
9900 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9901 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9902 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9903 // Adjust elevation of last track point
9904 if ( elev_new > last_tpt->altitude )
9906 elev_gain += elev_new - last_tpt->altitude;
9909 elev_loss += last_tpt->altitude - elev_new;
9914 // Display of the distance 'tooltip' during track creation is controlled by a preference
9916 if ( a_vik_get_create_track_tooltip() ) {
9918 gchar *str = distance_string (distance);
9920 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9921 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9922 pango_layout_set_text (pl, str, -1);
9924 pango_layout_get_pixel_size ( pl, &wd, &hd );
9927 // offset from cursor a bit depending on font size
9931 // Create a background block to make the text easier to read over the background map
9932 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9933 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9934 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9936 g_object_unref ( G_OBJECT ( pl ) );
9937 g_object_unref ( G_OBJECT ( background_block_gc ) );
9941 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9942 passalong->vtl = vtl;
9943 passalong->pixmap = pixmap;
9944 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9945 passalong->gc = vtl->current_track_newpoint_gc;
9949 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9951 // Update statusbar with full gain/loss information
9952 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9954 // draw pixmap when we have time to
9955 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9956 vtl->draw_sync_done = FALSE;
9957 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9959 return VIK_LAYER_TOOL_ACK;
9962 // NB vtl->current_track must be valid
9963 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9966 if ( vtl->current_track->trackpoints ) {
9967 // TODO rework this...
9968 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9969 GList *last = g_list_last(vtl->current_track->trackpoints);
9970 g_free ( last->data );
9971 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9973 vik_track_calculate_bounds ( vtl->current_track );
9977 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9979 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9980 // Bin track if only one point as it's not very useful
9981 if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
9982 if ( vtl->current_track->is_route )
9983 vik_trw_layer_delete_route ( vtl, vtl->current_track );
9985 vik_trw_layer_delete_track ( vtl, vtl->current_track );
9987 vtl->current_track = NULL;
9988 vik_layer_emit_update ( VIK_LAYER(vtl) );
9990 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9991 undo_trackpoint_add ( vtl );
9992 update_statusbar ( vtl );
9993 vik_layer_emit_update ( VIK_LAYER(vtl) );
10000 * Common function to handle trackpoint button requests on either a route or a track
10001 * . enables adding a point via normal click
10002 * . enables removal of last point via right click
10003 * . finishing of the track or route via double clicking
10005 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10009 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10012 if ( event->button == 2 ) {
10013 // As the display is panning, the new track pixmap is now invalid so don't draw it
10014 // otherwise this drawing done results in flickering back to an old image
10015 vtl->draw_sync_do = FALSE;
10019 if ( event->button == 3 )
10021 if ( !vtl->current_track )
10023 undo_trackpoint_add ( vtl );
10024 update_statusbar ( vtl );
10025 vik_layer_emit_update ( VIK_LAYER(vtl) );
10029 if ( event->type == GDK_2BUTTON_PRESS )
10031 /* subtract last (duplicate from double click) tp then end */
10032 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
10034 /* undo last, then end */
10035 undo_trackpoint_add ( vtl );
10036 vtl->current_track = NULL;
10038 vik_layer_emit_update ( VIK_LAYER(vtl) );
10042 tp = vik_trackpoint_new();
10043 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
10045 /* snap to other TP */
10046 if ( event->state & GDK_CONTROL_MASK )
10048 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10050 tp->coord = other_tp->coord;
10053 tp->newsegment = FALSE;
10054 tp->has_timestamp = FALSE;
10057 if ( vtl->current_track ) {
10058 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
10059 /* Auto attempt to get elevation from DEM data (if it's available) */
10060 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
10063 vtl->ct_x1 = vtl->ct_x2;
10064 vtl->ct_y1 = vtl->ct_y2;
10065 vtl->ct_x2 = event->x;
10066 vtl->ct_y2 = event->y;
10068 vik_layer_emit_update ( VIK_LAYER(vtl) );
10072 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10074 // if we were running the route finder, cancel it
10075 vtl->route_finder_started = FALSE;
10077 // ----------------------------------------------------- if current is a route - switch to new track
10078 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
10080 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
10081 if ( a_vik_get_ask_for_create_track_name() ) {
10082 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
10086 new_track_create_common ( vtl, name );
10089 return tool_new_track_or_route_click ( vtl, event, vvp );
10092 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10094 if ( event->button == 2 ) {
10095 // Pan moving ended - enable potential point drawing again
10096 vtl->draw_sync_do = TRUE;
10097 vtl->draw_sync_done = TRUE;
10101 /*** New route ****/
10103 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
10108 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10110 // if we were running the route finder, cancel it
10111 vtl->route_finder_started = FALSE;
10113 // -------------------------- if current is a track - switch to new route,
10114 if ( event->button == 1 && ( ! vtl->current_track ||
10115 (vtl->current_track && !vtl->current_track->is_route ) ) )
10117 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
10118 if ( a_vik_get_ask_for_create_track_name() ) {
10119 name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
10123 new_route_create_common ( vtl, name );
10126 return tool_new_track_or_route_click ( vtl, event, vvp );
10129 /*** New waypoint ****/
10131 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
10136 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10139 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10141 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
10142 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
10143 trw_layer_calculate_bounds_waypoints ( vtl );
10144 vik_layer_emit_update ( VIK_LAYER(vtl) );
10150 /*** Edit trackpoint ****/
10152 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
10154 tool_ed_t *t = g_new(tool_ed_t, 1);
10156 t->holding = FALSE;
10160 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
10166 * tool_edit_trackpoint_click:
10168 * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint
10169 * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information.
10170 * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point
10171 * then initiate the move operation to drag the point to a new destination.
10172 * NB The current trackpoint will get reset elsewhere.
10174 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10176 tool_ed_t *t = data;
10177 VikViewport *vvp = t->vvp;
10178 TPSearchParams params;
10180 params.x = event->x;
10181 params.y = event->y;
10182 params.closest_track_id = NULL;
10183 params.closest_tp = NULL;
10184 params.closest_tpl = NULL;
10185 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
10187 if ( event->button != 1 )
10190 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10193 if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) )
10196 if ( vtl->current_tpl )
10198 /* 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.) */
10199 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
10200 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
10202 current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id));
10207 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
10209 if ( current_tr->visible &&
10210 abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX &&
10211 abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) {
10212 marker_begin_move ( t, event->x, event->y );
10218 if ( vtl->tracks_visible )
10219 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms);
10221 if ( params.closest_tp )
10223 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
10224 vtl->current_tpl = params.closest_tpl;
10225 vtl->current_tp_id = params.closest_track_id;
10226 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
10227 trw_layer_tpwin_init ( vtl );
10228 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10229 vik_layer_emit_update ( VIK_LAYER(vtl) );
10233 if ( vtl->routes_visible )
10234 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, ¶ms);
10236 if ( params.closest_tp )
10238 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
10239 vtl->current_tpl = params.closest_tpl;
10240 vtl->current_tp_id = params.closest_track_id;
10241 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
10242 trw_layer_tpwin_init ( vtl );
10243 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10244 vik_layer_emit_update ( VIK_LAYER(vtl) );
10248 /* these aren't the droids you're looking for */
10252 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
10254 tool_ed_t *t = data;
10255 VikViewport *vvp = t->vvp;
10257 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10262 VikCoord new_coord;
10263 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10266 if ( event->state & GDK_CONTROL_MASK )
10268 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10269 if ( tp && tp != vtl->current_tpl->data )
10270 new_coord = tp->coord;
10272 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10275 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
10276 marker_moveto ( t, x, y );
10284 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10286 tool_ed_t *t = data;
10287 VikViewport *vvp = t->vvp;
10289 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10291 if ( event->button != 1)
10294 if ( t->holding ) {
10295 VikCoord new_coord;
10296 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10299 if ( event->state & GDK_CONTROL_MASK )
10301 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10302 if ( tp && tp != vtl->current_tpl->data )
10303 new_coord = tp->coord;
10306 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10307 if ( vtl->current_tp_track )
10308 vik_track_calculate_bounds ( vtl->current_tp_track );
10310 marker_end_move ( t );
10312 /* diff dist is diff from orig */
10314 my_tpwin_set_tp ( vtl );
10316 vik_layer_emit_update ( VIK_LAYER(vtl) );
10323 /*** Extended Route Finder ***/
10325 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10330 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10333 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10335 g_free ( new_end );
10336 vik_layer_emit_update ( VIK_LAYER(vtl) );
10338 /* remove last ' to:...' */
10339 if ( vtl->current_track->comment ) {
10340 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10341 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10342 gchar *new_comment = g_strndup ( vtl->current_track->comment,
10343 last_to - vtl->current_track->comment - 1);
10344 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10351 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10354 if ( !vtl ) return FALSE;
10355 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10356 if ( event->button == 3 && vtl->current_track ) {
10357 tool_extended_route_finder_undo ( vtl );
10359 else if ( event->button == 2 ) {
10360 vtl->draw_sync_do = FALSE;
10363 // if we started the track but via undo deleted all the track points, begin again
10364 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10365 return tool_new_track_or_route_click ( vtl, event, vvp );
10367 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10368 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10369 struct LatLon start, end;
10371 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10372 vik_coord_to_latlon ( &(tp_start->coord), &start );
10373 vik_coord_to_latlon ( &(tmp), &end );
10375 vtl->route_finder_started = TRUE;
10376 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
10378 // update UI to let user know what's going on
10379 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10380 VikRoutingEngine *engine = vik_routing_default_engine ( );
10382 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10385 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10386 vik_routing_engine_get_label ( engine ),
10387 start.lat, start.lon, end.lat, end.lon );
10388 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10390 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10393 /* Give GTK a change to display the new status bar before querying the web */
10394 while ( gtk_events_pending ( ) )
10395 gtk_main_iteration ( );
10397 gboolean find_status = vik_routing_default_find ( vtl, start, end );
10399 /* Update UI to say we're done */
10400 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10401 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10402 vik_routing_engine_get_label ( engine ),
10403 start.lat, start.lon, end.lat, end.lon )
10404 : g_strdup_printf ( _("Error getting route from %s."),
10405 vik_routing_engine_get_label ( engine ) );
10406 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10409 vik_layer_emit_update ( VIK_LAYER(vtl) );
10411 vtl->current_track = NULL;
10413 // create a new route where we will add the planned route to
10414 gboolean ret = tool_new_route_click( vtl, event, vvp );
10416 vtl->route_finder_started = TRUE;
10423 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10425 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10426 vtl->route_finder_started = FALSE;
10427 vtl->current_track = NULL;
10428 vik_layer_emit_update ( VIK_LAYER(vtl) );
10430 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10431 tool_extended_route_finder_undo ( vtl );
10438 /*** Show picture ****/
10440 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10445 /* Params are: vvp, event, last match found or NULL */
10446 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10448 if ( wp->image && wp->visible )
10450 gint x, y, slackx, slacky;
10451 GdkEventButton *event = (GdkEventButton *) params[1];
10453 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10454 slackx = wp->image_width / 2;
10455 slacky = wp->image_height / 2;
10456 if ( x <= event->x + slackx && x >= event->x - slackx
10457 && y <= event->y + slacky && y >= event->y - slacky )
10459 params[2] = wp->image; /* we've found a match. however continue searching
10460 * since we want to find the last match -- that
10461 * is, the match that was drawn last. */
10466 static void trw_layer_show_picture ( menu_array_sublayer values )
10468 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10470 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10471 #else /* WINDOWS */
10472 GError *err = NULL;
10473 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10474 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10475 g_free ( quoted_file );
10476 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10478 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() );
10479 g_error_free ( err );
10482 #endif /* WINDOWS */
10485 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10487 gpointer params[3] = { vvp, event, NULL };
10488 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10490 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10493 static menu_array_sublayer values;
10494 values[MA_VTL] = vtl;
10495 values[MA_MISC] = params[2];
10496 trw_layer_show_picture ( values );
10497 return TRUE; /* found a match */
10500 return FALSE; /* go through other layers, searching for a match */
10503 /***************************************************************************
10505 ***************************************************************************/
10508 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10510 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10511 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10514 /* Structure for thumbnail creating data used in the background thread */
10516 VikTrwLayer *vtl; // Layer needed for redrawing
10517 GSList *pics; // Image list
10518 } thumbnail_create_thread_data;
10520 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10522 guint total = g_slist_length(tctd->pics), done = 0;
10523 while ( tctd->pics )
10525 a_thumbnails_create ( (gchar *) tctd->pics->data );
10526 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10528 return -1; /* Abort thread */
10530 tctd->pics = tctd->pics->next;
10533 // Redraw to show the thumbnails as they are now created
10534 if ( IS_VIK_LAYER(tctd->vtl) )
10535 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10540 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10542 while ( tctd->pics )
10544 g_free ( tctd->pics->data );
10545 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10550 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10552 if ( ! vtl->has_verified_thumbnails )
10554 GSList *pics = NULL;
10555 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10558 gint len = g_slist_length ( pics );
10559 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10560 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10563 a_background_thread ( BACKGROUND_POOL_LOCAL,
10564 VIK_GTK_WINDOW_FROM_LAYER(vtl),
10566 (vik_thr_func) create_thumbnails_thread,
10568 (vik_thr_free_func) thumbnail_create_thread_free,
10576 static const gchar* my_track_colors ( gint ii )
10578 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10590 // Fast and reliable way of returning a colour
10591 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10594 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10596 GHashTableIter iter;
10597 gpointer key, value;
10601 g_hash_table_iter_init ( &iter, vtl->tracks );
10603 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10605 // Tracks get a random spread of colours if not already assigned
10606 if ( ! VIK_TRACK(value)->has_color ) {
10607 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10608 VIK_TRACK(value)->color = vtl->track_color;
10610 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10612 VIK_TRACK(value)->has_color = TRUE;
10615 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10618 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10624 g_hash_table_iter_init ( &iter, vtl->routes );
10626 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10628 // Routes get an intermix of reds
10629 if ( ! VIK_TRACK(value)->has_color ) {
10631 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10633 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10634 VIK_TRACK(value)->has_color = TRUE;
10637 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10644 * (Re)Calculate the bounds of the waypoints in this layer,
10645 * This should be called whenever waypoints are changed
10647 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10649 struct LatLon topleft = { 0.0, 0.0 };
10650 struct LatLon bottomright = { 0.0, 0.0 };
10653 GHashTableIter iter;
10654 gpointer key, value;
10656 g_hash_table_iter_init ( &iter, vtl->waypoints );
10658 // Set bounds to first point
10659 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10660 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10661 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10664 // Ensure there is another point...
10665 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10667 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10669 // See if this point increases the bounds.
10670 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10672 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10673 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10674 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10675 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10679 vtl->waypoints_bbox.north = topleft.lat;
10680 vtl->waypoints_bbox.east = bottomright.lon;
10681 vtl->waypoints_bbox.south = bottomright.lat;
10682 vtl->waypoints_bbox.west = topleft.lon;
10685 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10687 vik_track_calculate_bounds ( trk );
10690 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10692 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10693 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10696 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10698 if ( ! VIK_LAYER(vtl)->vt )
10701 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10702 if ( g_hash_table_size (vtl->tracks) > 1 )
10703 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10705 if ( g_hash_table_size (vtl->routes) > 1 )
10706 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10708 if ( g_hash_table_size (vtl->waypoints) > 1 )
10709 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10713 * Get the earliest timestamp available from all tracks
10715 static time_t trw_layer_get_timestamp_tracks ( VikTrwLayer *vtl )
10717 time_t timestamp = 0;
10718 GList *gl = g_hash_table_get_values ( vtl->tracks );
10719 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10720 gl = g_list_first ( gl );
10723 // Only need to check the first track as they have been sorted by time
10724 VikTrack *trk = (VikTrack*)gl->data;
10725 // Assume trackpoints already sorted by time
10726 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10727 if ( tpt && tpt->has_timestamp ) {
10728 timestamp = tpt->timestamp;
10730 g_list_free ( gl );
10736 * Get the earliest timestamp available from all waypoints
10738 static time_t trw_layer_get_timestamp_waypoints ( VikTrwLayer *vtl )
10740 time_t timestamp = 0;
10741 GList *gl = g_hash_table_get_values ( vtl->waypoints );
10743 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10744 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10745 if ( wpt->has_timestamp ) {
10746 // When timestamp not set yet - use the first value encountered
10747 if ( timestamp == 0 )
10748 timestamp = wpt->timestamp;
10749 else if ( timestamp > wpt->timestamp )
10750 timestamp = wpt->timestamp;
10753 g_list_free ( gl );
10759 * Get the earliest timestamp available for this layer
10761 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl )
10763 time_t timestamp_tracks = trw_layer_get_timestamp_tracks ( vtl );
10764 time_t timestamp_waypoints = trw_layer_get_timestamp_waypoints ( vtl );
10765 // NB routes don't have timestamps - hence they are not considered
10767 if ( !timestamp_tracks && !timestamp_waypoints ) {
10768 // Fallback to get time from the metadata when no other timestamps available
10770 if ( vtl->metadata && vtl->metadata->timestamp && g_time_val_from_iso8601 ( vtl->metadata->timestamp, >v ) )
10773 if ( timestamp_tracks && !timestamp_waypoints )
10774 return timestamp_tracks;
10775 if ( timestamp_tracks && timestamp_waypoints && (timestamp_tracks < timestamp_waypoints) )
10776 return timestamp_tracks;
10777 return timestamp_waypoints;
10780 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10782 if ( VIK_LAYER(vtl)->realized )
10783 trw_layer_verify_thumbnails ( vtl, vvp );
10784 trw_layer_track_alloc_colors ( vtl );
10786 trw_layer_calculate_bounds_waypoints ( vtl );
10787 trw_layer_calculate_bounds_tracks ( vtl );
10789 // Apply treeview sort after loading all the tracks for this layer
10790 // (rather than sorted insert on each individual track additional)
10791 // and after subsequent changes to the properties as the specified order may have changed.
10792 // since the sorting of a treeview section is now very quick
10793 // NB sorting is also performed after every name change as well to maintain the list order
10794 trw_layer_sort_all ( vtl );
10796 // Setting metadata time if not otherwise set
10797 if ( vtl->metadata ) {
10799 gboolean need_to_set_time = TRUE;
10800 if ( vtl->metadata->timestamp ) {
10801 need_to_set_time = FALSE;
10802 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10803 need_to_set_time = TRUE;
10806 if ( need_to_set_time ) {
10807 GTimeVal timestamp;
10808 timestamp.tv_usec = 0;
10809 timestamp.tv_sec = trw_layer_get_timestamp ( vtl );
10811 // No time found - so use 'now' for the metadata time
10812 if ( timestamp.tv_sec == 0 ) {
10813 g_get_current_time ( ×tamp );
10816 vtl->metadata->timestamp = g_time_val_to_iso8601 ( ×tamp );
10821 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10823 return vtl->coord_mode;
10827 * Uniquify the whole layer
10828 * Also requires the layers panel as the names shown there need updating too
10829 * Returns whether the operation was successful or not
10831 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10833 if ( vtl && vlp ) {
10834 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10835 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10836 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10842 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10844 vik_coord_convert ( &(wp->coord), *dest_mode );
10847 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10849 vik_track_convert ( tr, *dest_mode );
10852 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10854 if ( vtl->coord_mode != dest_mode )
10856 vtl->coord_mode = dest_mode;
10857 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10858 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10859 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10863 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10865 vtl->menu_selection = selection;
10868 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10870 return (vtl->menu_selection);
10873 /* ----------- Downloading maps along tracks --------------- */
10875 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10877 /* TODO: calculating based on current size of viewport */
10878 const gdouble w_at_zoom_0_125 = 0.0013;
10879 const gdouble h_at_zoom_0_125 = 0.0011;
10880 gdouble zoom_factor = zoom_level/0.125;
10882 wh->lat = h_at_zoom_0_125 * zoom_factor;
10883 wh->lon = w_at_zoom_0_125 * zoom_factor;
10885 return 0; /* all OK */
10888 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10890 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10891 (dist->lat >= ABS(to->north_south - from->north_south)))
10894 VikCoord *coord = g_malloc(sizeof(VikCoord));
10895 coord->mode = VIK_COORD_LATLON;
10897 if (ABS(gradient) < 1) {
10898 if (from->east_west > to->east_west)
10899 coord->east_west = from->east_west - dist->lon;
10901 coord->east_west = from->east_west + dist->lon;
10902 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10904 if (from->north_south > to->north_south)
10905 coord->north_south = from->north_south - dist->lat;
10907 coord->north_south = from->north_south + dist->lat;
10908 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10914 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10916 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10917 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10919 VikCoord *next = from;
10921 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10923 list = g_list_prepend(list, next);
10929 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10931 typedef struct _Rect {
10936 #define GLRECT(iter) ((Rect *)((iter)->data))
10939 GList *rects_to_download = NULL;
10942 if (get_download_area_width(vvp, zoom_level, &wh))
10945 GList *iter = tr->trackpoints;
10949 gboolean new_map = TRUE;
10950 VikCoord *cur_coord, tl, br;
10953 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10955 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10956 rect = g_malloc(sizeof(Rect));
10959 rect->center = *cur_coord;
10960 rects_to_download = g_list_prepend(rects_to_download, rect);
10965 gboolean found = FALSE;
10966 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10967 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10978 GList *fillins = NULL;
10979 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10980 /* seems that ATM the function get_next_coord works only for LATLON */
10981 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10982 /* fill-ins for far apart points */
10983 GList *cur_rect, *next_rect;
10984 for (cur_rect = rects_to_download;
10985 (next_rect = cur_rect->next) != NULL;
10986 cur_rect = cur_rect->next) {
10987 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10988 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10989 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10993 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10996 GList *fiter = fillins;
10998 cur_coord = (VikCoord *)(fiter->data);
10999 vik_coord_set_area(cur_coord, &wh, &tl, &br);
11000 rect = g_malloc(sizeof(Rect));
11003 rect->center = *cur_coord;
11004 rects_to_download = g_list_prepend(rects_to_download, rect);
11005 fiter = fiter->next;
11009 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
11010 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
11014 for (iter = fillins; iter; iter = iter->next)
11015 g_free(iter->data);
11016 g_list_free(fillins);
11018 if (rects_to_download) {
11019 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
11020 g_free(rect_iter->data);
11021 g_list_free(rects_to_download);
11025 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
11029 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
11030 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
11031 gint selected_zoom, default_zoom;
11033 VikTrwLayer *vtl = values[MA_VTL];
11034 VikLayersPanel *vlp = values[MA_VLP];
11036 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
11037 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
11039 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
11043 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
11045 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
11046 int num_maps = g_list_length(vmls);
11049 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
11053 // Convert from list of vmls to list of names. Allowing the user to select one of them
11054 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
11055 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
11057 gchar **np = map_names;
11058 VikMapsLayer **lp = map_layers;
11060 for (i = 0; i < num_maps; i++) {
11061 vml = (VikMapsLayer *)(vmls->data);
11063 *np++ = vik_maps_layer_get_map_label(vml);
11066 // Mark end of the array lists
11070 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
11071 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
11072 if (cur_zoom == zoom_vals[default_zoom])
11075 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
11077 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
11080 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
11083 for (i = 0; i < num_maps; i++)
11084 g_free(map_names[i]);
11086 g_free(map_layers);
11092 /**** lowest waypoint number calculation ***/
11093 static gint highest_wp_number_name_to_number(const gchar *name) {
11094 if ( strlen(name) == 3 ) {
11095 int n = atoi(name);
11096 if ( n < 100 && name[0] != '0' )
11098 if ( n < 10 && name[0] != '0' )
11106 static void highest_wp_number_reset(VikTrwLayer *vtl)
11108 vtl->highest_wp_number = -1;
11111 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
11113 /* if is bigger that top, add it */
11114 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
11115 if ( new_wp_num > vtl->highest_wp_number )
11116 vtl->highest_wp_number = new_wp_num;
11119 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
11121 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
11122 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
11123 if ( vtl->highest_wp_number == old_wp_num ) {
11125 vtl->highest_wp_number--;
11127 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11128 /* search down until we find something that *does* exist */
11130 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
11131 vtl->highest_wp_number--;
11132 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11137 /* get lowest unused number */
11138 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
11141 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
11143 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
11144 return g_strdup(buf);
11148 * trw_layer_create_track_list_both:
11150 * Create the latest list of tracks and routes
11152 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
11154 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
11155 GList *tracks = NULL;
11156 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
11157 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
11159 return vik_trw_layer_build_track_list_t ( vtl, tracks );
11162 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
11164 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11166 gchar *title = NULL;
11167 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
11168 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
11170 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
11172 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
11176 static void trw_layer_track_list_dialog ( menu_array_layer values )
11178 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11180 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
11181 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
11185 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
11187 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11189 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
11190 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );